当我在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很新,可以使用一些帮助。
答案 0 :(得分:5)
这是因为Q
安排"然后"回调到下一个tick(在承诺解决后)。见source。 "接下来打勾"反过来通过setImmediate
或setTimeout
或process.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)
嗯,确实是你用三个群组链接你的承诺,但是你并行运行每个链,所以流程如下:
fn1()
,安排fn2
和fn3
以便日后使用。fn1()
,安排相同的fn1()
,安排相同的fn2()
s run 如果您希望它们按顺序运行,则需要在同一个promise对象上安排它们。