如何正确地让nodejs有机会在我的无限while循环中运行其异步事件

时间:2019-06-25 06:51:17

标签: javascript mysql node.js events

每当我在位置B运行我的'save_to_db'函数时,它就会失败。

下面的代码显示了问题...


//location A

var i = 0;
while(true)
{

    i++;
    if(i == 100)
    {

//location B

        save_to_db(newUnit, 11214.1, 'TEST');
    }

}


//location C

函数“ save_to_db”在位置A和C处正常工作,但在位置B处失败。

我认为这是因为while循环是无限且同步的。因此,它不会给nodejs运行事件循环的机会。在这种情况下,如何给nodejs一个运行其事件循环的机会?

=====================更新========================= ======

上面的代码是该代码的非常简化的版本。我有一个名为“ db_util”的类/模块,它有两个方法,“ save_to_db”和“ read_from_db”。我认为由于这些方法使用了“ mysql”模块,因此它们以异步方式访问数据库。但是我的while true循环阻止了nodejs eventloop。因此,while循环中的任何方法都没有机会被调用。

var mysql = require('mysql');
var db_util = function()
{}

db_util.prototype = {
    save_to_db: function (neural_network, fitness, type) {
        var connection = mysql.createConnection({
            host: 'localhost',
            user: 'root',
            password: '',
            database: 'nodejstestdb'
        });
        connection.connect();      
        var addSql = 'INSERT INTO mytable(id, nn, type, fitness) VALUES(?,?,?,?)';
        var addSqlParams = [null, neural_network, type, fitness];
        connection.query(addSql, addSqlParams, function (err, result) {
            if (err) {
                console.log('[INSERT ERROR] - ', err.message);
                return;
            }                  
            console.log('INSERT ID:', result);          
        });
        connection.end();
    },


    read_from_db:function(){

        var connection = mysql.createConnection({
            host: 'localhost',
            user: 'root',
            password: '',
            database: 'nodejstestdb'
        });
        connection.connect();

        var sql = 'SELECT * FROM mytable ORDER BY fitness DESC'; 
        connection.query(sql, function (err, result) {
            if (err) {
                console.log('[SELECT ERROR] - ', err.message);
                return;
            }
            console.log(result);
            var nn = result[0].nn;            
            neural_network = synaptic.Network.fromJSON(JSON.parse(nn));           
            return neural_network;
        });
        connection.end();
    }
}

module.exports = db_util;


=================由于答案而更新2

let i = 0;
(function tick() {
    ++i;
    if (i%100 == 0) {
        save_to_db();
    }
    setTimeout(tick, 0); // Queue a callback on next(ish) event loop cycle
}());
let i = 0;
tick();

@ T.J。人群,主席先生,谢谢您的回答。但是您上面的代码和下面的代码有什么区别?

var i = 0;

function tick() {
    ++i;
    if (i%100 == 0) {
        save_to_db();
    }
    setTimeout(tick, 0); // Queue a callback on next(ish) event loop cycle
}

tick();

1 个答案:

答案 0 :(得分:1)

As VLAZ says,我想知道为什么需要无限循环,而不仅仅是使用事件循环。

  

我认为这是因为while循环是无限且同步的。因此,它没有给nodejs运行事件循环的机会。

是的,您完全正确。

不知道save_to_db的工作原理很难回答这个问题,但是一般来说,您有以下三种选择:

  1. 使用一系列链接的setTimeout(或setImmediate)回调。

    • 变化:您要做一个工作块(例如,最多进行100次迭代),然后使用setTimeout安排下一个工作块。
  2. 使用async函数和await(如果save_to_db返回承诺,或者可以对其进行修改以返回承诺)。

  3. 使用worker thread,它在应该执行save_to_db时将消息发布到主线程(然后,阻塞工作线程的事件循环不会阻止主线程上的事件save_to_db所需)。 (我可能不会为此使用工人。)


#1大致如下:

let i = 0;
(function tick() {
    ++i;
    if (i == 100) {
        save_to_db();
    }
    setTimeout(tick, 0); // Queue a callback on next(ish) event loop cycle
}());

请注意,Node.js设置了限时计时器,因此setTimeout(tick, 0)不一定会在下一个事件循环迭代中安排下一个调用。您可以改用setImmediate,因为它

  

计划在I / O事件的回调之后“立即”执行回调。

例如,此代码:

let i = 0;
let last = Date.now();
let sum = 0;
(function tick() {
    ++i;
    sum += Date.now() - last;
    if (i < 1000) {
        last = Date.now();
        setTimeout(tick, 0);
    } else {
        console.log(`Average: ${sum / i}`);
    }
})();

报告在Linux机器上安装Node v12.4的平均经过时间为1.155ms。使用setImmediate的等效代码报告的平均时间仅为0.004ms。

您在评论中说过,您不理解如何在“块”中实现这一点。这是一个示例:

const chunkSize = 1000; // Or whatever
let i = 0;
(function chunk() {
    const chunkEnd = i + chunkSize;
    while (i++ < chunkEnd) {
        if (/*time to save to the DB*/) {
            // Note that we don't wait for it to complete, you've said it's
            // okay not to wait and that it's okay if they overlap
            save_to_db();
        }
    }
    setImmediate(chunk, 0);
})();

这样做,它将在主线程上完成大量工作,然后产生以允许处理save_to_db可能需要的任何I / O回调,然后继续进行。您可以对其进行一些优化,以仅在知道save_to_db仍有工作要做时才退让,但这可能是对其进行了过度设计。


#2大致如下:

(async () => {
    try {
        let i = 0;
        while (true) {
            if (i == 100) {
                await save_to_db();
            }
        }
    } catch (e) {
        // Handle/report error
    }
})();

#3我将留给读者练习,但我可能不会为此使用工人。