简体   繁体   English

NodeJS承诺阻止请求

[英]NodeJS promise blocking requests

I am quite confused about why is my promise blocking the node app requests. 我对为什么诺言阻止节点应用程序请求感到困惑。

Here is my simplified code: 这是我的简化代码:

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'));
});

And the module: 和模块:

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;

Obviously this is very simplified. 显然,这是非常简化的。 The promise function does some work that takes anywhere from 5 to 30 minutes and has to run only when server starts up. Promise函数执行的工作可能需要5到30分钟,并且仅在服务器启动时才能运行。 There is NO async operation in that promise function. 该Promise函数中没有异步操作。 Its just a lot of data processing, loops etc. 它只是大量的数据处理,循环等。

I wont to be able to do requests right away though. 我不会马上就可以提出要求。 So what I expect is when I run the server, I can go right away to 127.0.0.1:3000 and see Main and same for any other requests. 因此,我期望的是在运行服务器时,我可以立即转到127.0.0.1:3000并查看Main和其他请求。

Eventually I want to see the progress of that task by accessing /status but Im sure I can make that work once the server works as expected. 最终,我想通过访问/status查看该任务的进度,但是我确定一旦服务器按预期工作,我就可以使该工作正常进行。

At the moment, when I open / it just hangs until the promise job finishes.. 目前,当我打开/它会一直挂起,直到答应工作完成。

Obviously im doing something wrong... 显然我在做错事...

The main thread of Javascript in node.js is single threaded. node.js中Javascript的主线程是单线程。 So, if you do some giant loop that is processor bound, then that will hog the one thread and no other JS will run in node.js until that one operation is done. 因此,如果您执行某个受处理器限制的巨型循环,那么它将占用一个线程,并且其他的JS将不会在node.js中运行,直到完成一个操作为止。

So, when you call: 因此,当您致电:

someModule.doSomething()

and that is all synchronous, then it does not return until it is done executing and thus the lines of code following that don't execute until the doSomething() method returns. 并且这都是同步的,那么它直到执行完毕才返回,因此紧接着doSomething()方法返回的代码行才执行。 And, just so you understand, the use of promises with synchronous CPU-hogging code does not help your cause at all. 而且,正如您了解的那样,将诺言与同步CPU跳转代码一起使用根本无法解决您的问题。 If it's synchronous and CPU bound, it's just going to take a long time to run before anything else can run. 如果它是同步的且受CPU限制,则将需要很长时间才能运行其他任何东西。

If there is I/O involves in the loop (like disk I/O or network I/O), then there are opportunities to use async I/O operations and make the code non-blocking. 如果循环中涉及I / O(例如磁盘I / O或网络I / O),那么就有机会使用异步I / O操作并使代码无阻塞。 But, if not and it's just a lot of CPU stuff, then it will block until done and no other code will run. 但是,如果不是这样,而这只是大量的CPU工作,那么它将阻塞直到完成,并且不会运行其他代码。

Your opportunities for changing this are: 您改变的机会是:

  1. Run the CPU consuming code in another process. 在另一个进程中运行占用CPU的代码。 Either create a separate program that you run as a child process that you can pass input to and get output from or create a separate server that you can then make async requests to. 创建一个作为子进程运行的单独程序,您可以将其传递给输入或从中获取输出,或者创建一个单独的服务器,然后向其发出异步请求。

  2. Break the non-blocking work into chunks where you execute 100ms chunks of work at a time, then yield the processor back to the event loop (using something like setTimeout() to allow other things in the event queue to be serviced and run before you pick up and run the next chunk of work. You can see Best way to iterate over an array without blocking the UI for ideas on how to chunk synchronous work. 将非阻塞工作分成多个块,您一次执行100ms的块工作,然后使处理器返回事件循环(使用setTimeout()东西来允许事件队列中的其他事件得到服务并在运行之前运行)选择并运行下一个工作块,您将看到在不阻塞UI的情况下遍历数组的最佳方法,以获取有关如何对同步工作进行分块的想法。

As an example, you could chunk your current loop. 例如,您可以对当前循环进行分块。 This runs up to 100ms of cycles and then breaks execution to give other things a chance to run. 这需要长达100毫秒的周期,然后中断执行,使其他事物有运行的机会。 You can set the cycle time to whatever you want. 您可以将循环时间设置为任意时间。

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

If your task is IO-bound go with process.nextTick . 如果您的任务是IO绑定,请使用process.nextTick If your task is CPU-bound asynchronous calls won't offer much performance-wise. 如果您的任务是与CPU绑定,那么异步调用将无法提供很多性能。 In that case you need to delegate the task to another process. 在这种情况下,您需要将任务委托给另一个进程。 An example solution would be to spawn a child process, do the work and pipe the results back to the parent process when done. 一个示例解决方案是生成一个子进程,进行工作并将完成后的结果通过管道传递给父进程。

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

If your application needs to do this often then forking lots of child processes quickly becomes a resource hog - each time you fork, a new V8 process will be loaded into memory. 如果您的应用程序需要经常执行此操作,那么派生许多子进程会很快成为资源浪费-每次您分叉时,都会将一个新的V8进程加载到内存中。 In this case it is probably better to use one of the multiprocessing modules like Node's own Cluster . 在这种情况下,最好使用像Node自己的Cluster这样的多处理模块之一。 This module offers easy creation and communication between master-worker processes and can remove a lot of complexity from your code. 该模块可轻松创建主工作流程,并在主工作流程之间进行通信,并且可以消除代码中的许多复杂性。

See also a related question: Node.js - Sending a big object to child_process is slow 另请参阅相关问题: Node.js-将大对象发送到child_process很慢

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

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