繁体   English   中英

NodeJS承诺阻止请求

[英]NodeJS promise blocking requests

我对为什么诺言阻止节点应用程序请求感到困惑。

这是我的简化代码:

var express = require('express');    
var someModule = require('somemodule');

app = express();

app.get('/', function (req, res) {        
    res.status(200).send('Main');
});

app.get('/status', function (req, res) {
    res.status(200).send('Status');
});         

// Init Promise
someModule.doSomething({}).then(function(){},function(){}, function(progress){
    console.log(progress);        
});

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Example app listening at http://%s:%s in %s environment',host, port, app.get('env'));
});

和模块:

var q = require('q');

function SomeModule(){
    this.doSomething = function(){
        return q.Promise(function(resolve, reject, notify){            
            for (var i=0;i<10000;i++){
               notify('Progress '+i);
            }
            resolve();
        });
    }          
}

module.exports = SomeModule;

显然,这是非常简化的。 Promise函数执行的工作可能需要5到30分钟,并且仅在服务器启动时才能运行。 该Promise函数中没有异步操作。 它只是大量的数据处理,循环等。

我不会马上就可以提出要求。 因此,我期望的是在运行服务器时,我可以立即转到127.0.0.1:3000并查看Main和其他请求。

最终,我想通过访问/status查看该任务的进度,但是我确定一旦服务器按预期工作,我就可以使该工作正常进行。

目前,当我打开/它会一直挂起,直到答应工作完成。

显然我在做错事...

node.js中Javascript的主线程是单线程。 因此,如果您执行某个受处理器限制的巨型循环,那么它将占用一个线程,并且其他的JS将不会在node.js中运行,直到完成一个操作为止。

因此,当您致电:

someModule.doSomething()

并且这都是同步的,那么它直到执行完毕才返回,因此紧接着doSomething()方法返回的代码行才执行。 而且,正如您了解的那样,将诺言与同步CPU跳转代码一起使用根本无法解决您的问题。 如果它是同步的且受CPU限制,则将需要很长时间才能运行其他任何东西。

如果循环中涉及I / O(例如磁盘I / O或网络I / O),那么就有机会使用异步I / O操作并使代码无阻塞。 但是,如果不是这样,而这只是大量的CPU工作,那么它将阻塞直到完成,并且不会运行其他代码。

您改变的机会是:

  1. 在另一个进程中运行占用CPU的代码。 创建一个作为子进程运行的单独程序,您可以将其传递给输入或从中获取输出,或者创建一个单独的服务器,然后向其发出异步请求。

  2. 将非阻塞工作分成多个块,您一次执行100ms的块工作,然后使处理器返回事件循环(使用setTimeout()东西来允许事件队列中的其他事件得到服务并在运行之前运行)选择并运行下一个工作块,您将看到在不阻塞UI的情况下遍历数组的最佳方法,以获取有关如何对同步工作进行分块的想法。

例如,您可以对当前循环进行分块。 这需要长达100毫秒的周期,然后中断执行,使其他事物有运行的机会。 您可以将循环时间设置为任意时间。

function SomeModule(){
    this.doSomething = function(){
        return q.Promise(function(resolve, reject, notify){
            var cntr = 0, numIterations = 10000, timePerSlice = 100;
            function run() {
                if (cntr < numIterations) {
                    var start = Date.now();
                    while (Date.now() - start < timePerSlice && cntr < numIterations) {
                        notify('Progress '+cntr);
                        ++cntr;
                    }
                    // give some other things a chance to run and then call us again
                    // setImmediate() is also an option here, but setTimeout() gives all
                    // other operations a chance to run alongside this operation
                    setTimeout(run, 10);
                } else {
                    resolve();
                }
            }
            run();
        });
    }
}

如果您的任务是IO绑定,请使用process.nextTick 如果您的任务是与CPU绑定,那么异步调用将无法提供很多性能。 在这种情况下,您需要将任务委托给另一个进程。 一个示例解决方案是生成一个子进程,进行工作并将完成后的结果通过管道传递给父进程。

有关更多信息,请参见nodejs.org/api/child_process.html。

如果您的应用程序需要经常执行此操作,那么派生许多子进程会很快成为资源浪费-每次您分叉时,都会将一个新的V8进程加载到内存中。 在这种情况下,最好使用像Node自己的Cluster这样的多处理模块之一。 该模块可轻松创建主工作流程,并在主工作流程之间进行通信,并且可以消除代码中的许多复杂性。

另请参阅相关问题: Node.js-将大对象发送到child_process很慢

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM