как правильно позволить nodejs иметь возможность запускать свои асинхронные события в моем цикле while - PullRequest
0 голосов
/ 25 июня 2019

Всякий раз, когда я запускаю свою функцию «save_to_db» в местоположении B, она не срабатывает.

Код ниже показывает проблему ...


//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 бесконечен и синхронизирован. Следовательно, это не дает узлу возможность запустить цикл обработки событий. Как мы можем должным образом дать нодзйс шанс запустить цикл обработки событий в этом случае?

===================== Обновления ======================== ======

Код выше является очень упрощенной версией кода. У меня есть класс / модуль с именем «db_util», и у него есть два метода, «save_to_db» и «read_from_db». Я думаю, что из-за этих методов используется модуль «mysql», они обращаются к базе данных асинхронным способом. Но мой пока истинный цикл блокирует 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. Crowder, сэр, спасибо за ваш ответ. Но в чем разница между вашим кодом выше и кодом ниже?

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 Ответ

1 голос
/ 25 июня 2019

Как говорит ВЛАЗ , мне интересно, зачем вам нужен бесконечный цикл, а не просто использование цикла событий.

Я думаю, это потому, что цикл while бесконечен и синхронизирован. Поэтому он не дает нодзйсу шанс запустить цикл обработки событий.

Да, вы абсолютно правы.

Трудно ответить на этот вопрос, не зная, как работает save_to_db, но в целом у вас есть три варианта:

  1. Используйте цепочку обратных вызовов setTimeout (или setImmediate).

    • Вариация: Вы выполняете блок работы (скажем, до 100 итераций), а затем используете setTimeout для планирования следующего блока.
  2. Используйте функцию async и await (если save_to_db возвращает обещание или может быть изменено для возврата обещания).

  3. Используйте рабочий поток , который отправляет сообщение в основной поток, когда он должен выполнить save_to_db (тогда блокировка цикла событий рабочего потока не предотвращает события в главном потоке нить, которая нужна * 1037). (Я, вероятно, не использовал бы работника для этого.)


# 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, потому что это

Планирует «немедленное» выполнение обратного вызова после обратных вызовов событий ввода / вывода.

Например, этот код:

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}`);
    }
})();

сообщает о среднем истекшем времени 1,155 мс на моей установке Node v12.4 на моем компьютере с Linux. Эквивалентный код, использующий setImmediate, сообщает в среднем всего 0,004 мс.

Вы сказали в комментарии, что не понимаете, как реализовать это в «чанках». Вот пример:

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, а затем продолжает работу. Вы могли бы немного оптимизировать его, чтобы получить отдачу только тогда, когда он знает, что save_to_db еще есть над чем поработать, но это, вероятно, его слишком много.


# 2 выглядит примерно так:

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

# 3 Я оставлю это упражнение для читателя, но, вероятно, я бы не использовал для этого работника.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...