簡體   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