NodeJs как создать неблокирующее вычисление - PullRequest
16 голосов
/ 03 марта 2012

Я пытаюсь разобраться с созданием неблокирующего фрагмента тяжелых вычислений в nodejs. Возьмите этот пример (лишенный других вещей):

http.createServer(function(req, res) {
    console.log(req.url);
    sleep(10000);
    res.end('Hello World');
}).listen(8080, function() { console.log("ready"); });

Как вы можете себе представить, если я открою 2 окна браузера одновременно, первое будет ждать 10 секунд, а другое - 20, как и ожидалось. Итак, вооружившись знанием того, что обратный вызов каким-то образом асинхронный, я удалил сон и вместо этого поставил:

doHeavyStuff(function() {
    res.end('Hello World');
});

с простой функцией:

function doHeavyStuff(callback) {
    sleep(10000);
    callback();
}

это, конечно, не работает ... Я также пытался определить EventEmitter и зарегистрировать его, но, например, основная функция Emitter находится в спящем режиме перед тем, как выдать 'done', так что снова все будет работать блок.

Мне интересно, как другие люди пишут неблокирующий код ... например, модуль mongojs или child_process.exec не блокируют, что означает, что где-то внизу кода они либо форкируют процесс в другом потоке, и слушать его события. Как я могу воспроизвести это в методе, который, например, имеет длительный процесс?

Полностью ли я неправильно понимаю парадигму nodejs? : /

Спасибо!

Обновление: решение (вроде)

Спасибо за ответ Линусу, действительно, единственный способ - создать дочерний процесс, например, скрипт другого узла:

http.createServer(function(req, res) {
    console.log(req.url);

    var child = exec('node calculate.js', function (err, strout, strerr) {
        console.log("fatto");
        res.end(strout);
    });

}).listen(8080, function() { console.log("ready"); });

Calculate.js может занять некоторое время, чтобы сделать то, что ему нужно, и вернуться. Таким образом, несколько запросов будут выполняться параллельно, так сказать.

Ответы [ 4 ]

12 голосов
/ 03 марта 2012

Вы не можете сделать это напрямую, не используя некоторые модули ввода-вывода в узле (например, fs или net).Если вам нужно выполнить длительные вычисления, я предлагаю вам сделать это в дочернем процессе (например, child_process.fork) или в очереди.

5 голосов
/ 09 августа 2017

Мы (Microsoft) только что выпустили napajs , который может работать с Node.js для включения многопоточных сценариев JavaScript в одном и том же процессе.

Ваш код будет выглядеть следующим образом:

var napa = require('napajs');

// One-time setup. 
// You can change number of workers per your requirement. 
var zone = napa.zone.create('request-worker-pool', { workers: 4 });

http.createServer(function(req, res) {
    console.log(req.url);

    zone.execute((request) => {
        var result = null;
        // Do heavy computation to get result from request
        // ...
        return result;
    }, [req]).then((result) => {
        res.end(result.value);
    }
}).listen(8080, function() { console.log("ready"); });

Вы можете прочитать этот пост для более подробной информации.

4 голосов
/ 28 февраля 2014

Это классическое недопонимание того, как работает цикл обработки событий.

Это не то, что является уникальным для узла - если у вас есть длительные вычисления в браузере, он также заблокируется.Способ сделать это состоит в том, чтобы разбить вычисление на маленьких порций, которые приводят к выполнению цикла обработки событий , позволяя среде JS чередоваться с другими конкурирующими вызовами, но за один раз происходит только одно,

Демонстрация setImmediate может быть поучительной, которую вы можете найти здесь .

3 голосов
/ 03 марта 2012

Если ваши вычисления можно разбить на куски, вы можете запланировать, что исполнитель будет запрашивать данные каждые N секунд, а затем через M секунд снова запускаться. Или порождайте выделенного потомка для этой задачи, чтобы основной поток не блокировался.

...