如何在node.js中创build后台单线程FIFO作业队列

我是一个JS /节点初学者,我试图让我的头绕着Javascript的“并发”的主题。 我觉得我现在对callback比较舒服,但我不认为这是我的scheme。 基本上我经常需要一个接一个地处理昂贵的任务( worker ),而main process继续工作。 这是一个最小的testing。

 /* * We have a worker function that does some expensive task, eg, an * I/O task or something else. */ worker = function ( jobId ) { // It will take something between 1 and 2 seconds. var runtime = Math.floor((Math.random() * 1000) + 1000); console.log("started job #" + jobId + " (" + runtime + " ms)"); // Then the worker will do something for a while ... setTimeout( function() { // .. and at some point it'll be finished. console.log("finished job #" + jobId); }, runtime ); }; /* * We obviously have a main process that meanwhile does other stuff * like processed user interactions. In this case we call this until * some artificial tickets are used. */ mainprocess = function (tickets) { // Simulate some processing time .. var runtime = Math.floor((Math.random() * 500)); setTimeout( function() { console.log("main process #" + tickets + " (" + runtime + " ms)"); if (tickets > 0) { tickets--; mainprocess(tickets); } }, runtime ); } // At some point in the code we create workers and we want to make sure // they're processed in the *order of creation* and *one after another* // without blocking the main process ... for ( var i = 1; i <= 10; i++) { worker(i); }; // ... and the some other stuff will happen for a while! mainprocess(10); // .. 

代码目前输出的东西像..

 started job #1 (1751 ms) started job #2 (1417 ms) ... started job #9 (1050 ms) started job #10 (1864 ms) main process #10 (142 ms) main process #9 (228 ms) main process #8 (149 ms) main process #7 (88 ms) main process #6 (410 ms) finished job #9 finished job #5 main process #5 (265 ms) finished job #2 main process #4 (270 ms) finished job #7 finished job #3 finished job #1 ... main process #1 (486 ms) main process #0 (365 ms) 

我不知道如何改变代码,这样主进程将继续,而工作线程按照创build顺序执行(目前只能以正确的顺序启动), 一个接一个地执行 (目前全部并行) 。 期望的输出将是..

 started job #1 (1384 ms) main process #10 (268 ms) main process #9 (260 ms) main process #8 (216 ms) main process #7 (93 ms) main process #6 (160 ms) main process #5 (269 ms) main process #4 (44 ms) finished job #1 started job #2 (1121 ms) main process #3 (172 ms) main process #2 (170 ms) main process #1 (437 ms) finished job #2 started job #3 (1585 ms) main process #0 (460 ms) finished job #3 started job #4 (1225 ms) finished job #4 started job #5 (1300 ms) finished job #5 

任何帮助,将不胜感激。

好的,我明白了。 扩展和评论的代码使用JavaScript的内置承诺 ,但是您可以使用Q或Bluebird或任何其他节点兼容的承诺库实现相同的function。 请注意, jquery $.Deferred对象在节点环境中不可用。

 /* * We have a worker function that does some expensive task, eg, an * I/O task or something else. */ worker = function ( jobId ) { // we need to put it into a new promise object return new Promise(function(resolve, reject) { // It will take something between 1 and 2 seconds. var runtime = Math.floor((Math.random() * 1000) + 1000); console.log("started job #" + jobId + " (" + runtime + " ms)"); // Then the worker will do something for a while ... setTimeout( function() { // .. and at some point it'll be finished. console.log("finished job #" + jobId); // .. now we have to resolve the promise!! resolve("resolved job #" + jobId); }, runtime ); }); }; /* * We obviously have a main process that meanwhile does other stuff * like processed user interactions. In this case we call this until * some artificial tickets are used. */ mainprocess = function (tickets) { // Simulate some processing time .. var runtime = Math.floor((Math.random() * 500)); setTimeout( function() { console.log("main process #" + tickets + " (" + runtime + " ms)"); if (tickets > 0) { tickets--; mainprocess(tickets); } }, runtime ); } // create a sequence with a resolved promise var sequence = Promise.resolve(); // At some point in the code we create workers and we want to make sure // they're processed in the *order of creation* and *one after another* // without blocking the main process ... for ( var i = 1; i <= 10; i++) { // create an IIFE so that the current "i" gets its own // closure when it will be used later (otherwise all job ids // would be "11" on invokation of the worker). (function() { var jobId = i; // add a new promise after the previous promise resolved sequence = sequence.then( function(result) { // handle result later return worker(jobId); // return just added the next promise to the chain! }, function(err) { // handle error later } ); })(); // END IIFE }; // ... and the some other stuff will happen for a while! mainprocess(10); // .. 

输出符合要求:

 started job #1 (1384 ms) main process #10 (268 ms) main process #9 (260 ms) main process #8 (216 ms) main process #7 (93 ms) main process #6 (160 ms) main process #5 (269 ms) main process #4 (44 ms) finished job #1 started job #2 (1121 ms) main process #3 (172 ms) main process #2 (170 ms) main process #1 (437 ms) finished job #2 started job #3 (1585 ms) main process #0 (460 ms) finished job #3 started job #4 (1225 ms) finished job #4 started job #5 (1300 ms) finished job #5 ... 

我必须对以下文章给予很高的评价:

  • JavaScript的承诺 – 在那里又回来了
  • 我们有承诺的问题
  • 总承诺资源(供进一步阅读)

如果你希望主线程先运行,然后工作线程按照它们创build的顺序运行,那么把你正在调用worker(i)的for循环放在一个设置的Timeout函数中,给出一个大约1000ms的常量延迟。 那么完成的工作也应该被给予1000毫秒的恒定延迟,以便它们以创build的顺序执行。 检查链接: jsfiddle.net/som99/weq87xnd/2/