使用node.js在每个循环中使用deferred promise

时间:2015-01-18 11:12:56

标签: node.js promise cheerio

当我在node.js的循环中使用promise时,我遇到了一个问题。 下面是一个简单的例子,我使用cheerio来抓取一个网页并执行以下操作:

$('.xyz').each(function(){
  fn1()
  .then(fn2)
  .then(fn3)    
});


function fn1() {
  var deferred = Q.defer();
  console.log("1");
  deferred.resolve();
  return deferred.promise;
}

function fn2() {
  var deferred = Q.defer();
  console.log("2");
  deferred.resolve();
  return deferred.promise;
}

function fn3() {
  var deferred = Q.defer();
  console.log("3");
  deferred.resolve();
  return deferred.promise;
}

我期待最终输出为123123123,但我得到111222333.有人可以解释为什么会这样。我对node.js很新,可以使用一些帮助。

3 个答案:

答案 0 :(得分:5)

这是因为Q安排"然后"回调到下一个tick(在承诺解决后)。见source。 "接下来打勾"反过来通过setImmediatesetTimeoutprocess.nextTick等来实现,具体取决于您使用的平台(请参阅source)。所有promises实现保证在当前调用堆栈执行后执行then回调。可以通过向微任务队列(如v8引擎)或事件循环任务队列(例如,使用setTimeout)添加回调来实现。如果您不理解事件循环任务队列的工作原理 - 请参阅this video

答案 1 :(得分:2)

排队承诺处理程序和重新声明承诺之间存在细微差别。请考虑以下代码,它会产生您想要的行为,即123123123:

var a = Q();

[1,2,3].forEach(function(){
  a = a.then(fn1)
    .then(fn2)
    .then(fn3);
});


function fn1() {
  var deferred = Q.defer();
  console.log("1");
  deferred.resolve();
  return deferred.promise;
}

function fn2() {
  var deferred = Q.defer();
  console.log("2");
  deferred.resolve();
  return deferred.promise;
}

function fn3() {
  var deferred = Q.defer();
  console.log("3");
  deferred.resolve();
  return deferred.promise;
}

(JSBin:http://jsbin.com/hituzedare/2/edit?js,console

现在考虑以下非常相似的例子,结果是111222333。

var a = Q();

[1,2,3].forEach(function(){
  a.then(fn1)
    .then(fn2)
    .then(fn3);
});


function fn1() {
  var deferred = Q.defer();
  console.log("1");
  deferred.resolve();
  return deferred.promise;
}

function fn2() {
  var deferred = Q.defer();
  console.log("2");
  deferred.resolve();
  return deferred.promise;
}

function fn3() {
  var deferred = Q.defer();
  console.log("3");
  deferred.resolve();
  return deferred.promise;
}

(JSBin:http://jsbin.com/hituzedare/1/edit?js,console

问题是:第一个示例将变量重新定义为新的承诺 - 特别是fn3返回的承诺。

因此,在下一个forEach次迭代中,承诺现在被链接到fn3的末尾,而不是最高的承诺。

我希望这有帮助!

答案 2 :(得分:0)

嗯,确实是你用三个群组链接你的承诺,但是你并行运行每个链,所以流程如下:

  1. 运行fn1(),安排fn2fn3以便日后使用。
  2. 再次运行fn1(),安排相同的
  3. 再次运行fn1(),安排相同的
  4. 其中一个fn2() s run
  5. 等...
  6. 如果您希望它们按顺序运行,则需要在同一个promise对象上安排它们。