在测试中,我发现JavaScript Promises 总是异步,无论它们是否在其链中包含任何异步函数。
以下是一些显示控制台操作顺序的代码。如果运行它,您将看到即使每个函数都是同步的,输出也会显示两个aPromise()
调用并行运行,而"surprisingly this happens after run 2 finishes"
不在运行2完成之前完成
function aPromise() {
return new Promise(function(resolve, reject) {
console.log("making promise A")
resolve(bPromise());
console.log("promise A resolved")
});
}
function bPromise() {
return new Promise(function(resolve, reject) {
console.log("making and resolving promise B")
resolve();
});
}
aPromise().then(function() {
console.log("finish run 1");
}).then(function() {
console.log("surprisingly this happens after run 2 finishes");
});
aPromise().then(function() {
console.log("finish run 2");
})
输出到控制台:
making promise A
making and resolving promise B
promise A resolved
making promise A
making and resolving promise B
promise A resolved
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
那么,为什么在只调用同步函数时JavaScript承诺异步?背后会发生什么导致这种行为?
P.S。为了更好地理解这一点,我实现了自己的Promise系统,我发现使同步函数以预期的顺序发生很容易,但是让它们并行发生是我只能通过在每个时间放置几毫秒的setTimeout()来实现的。解决(我的猜测是,这不是vanilla承诺发生的事情,而且它们实际上是多线程的。)
对于我的一个程序来说,这是一个小问题,我正在遍历一个树,将一个函数数组应用于每个节点,如果该节点已经运行了异步函数,则将这些函数放入队列中。大多数函数是同步的,因此很少使用队列,但是从回调(地狱)切换到Promises时,我一直遇到一个问题,即队列几乎总是被使用,因为Promises从不同步运行。这不是一个大问题,但这是一个调试噩梦。
1年后编辑
我最后编写了一些代码来处理这个问题。这并不是非常彻底,但我已成功地用它来解决我遇到的问题。
var SyncPromise = function(fn) {
var syncable = this;
syncable.state = "pending";
syncable.value;
var wrappedFn = function(resolve, reject) {
var fakeResolve = function(val) {
syncable.value = val;
syncable.state = "fulfilled";
resolve(val);
}
fn(fakeResolve, reject);
}
var out = new Promise(wrappedFn);
out.syncable = syncable;
return out;
}
SyncPromise.resolved = function(result) {
return new SyncPromise(function(resolve) { resolve(result); });
}
SyncPromise.all = function(promises) {
for(var i = 0; i < promises.length; i++) {
if(promises[i].syncable && promises[i].syncable.state == "fulfilled") {
promises.splice(i, 1);
i--;
}
// else console.log("syncable not fulfilled" + promises[i].syncable.state)
}
if(promises.length == 0)
return SyncPromise.resolved();
else
return new SyncPromise(function(resolve) { Promise.all(promises).then(resolve); });
}
Promise.prototype.syncThen = function (nextFn) {
if(this.syncable && this.syncable.state == "fulfilled") {
//
if(nextFn instanceof Promise) {
return nextFn;
}
else if(typeof nextFn == "function") {
var val = this.syncable.value;
var out = nextFn(val);
return new SyncPromise(function(resolve) { resolve(out); });
}
else {
PINE.err("nextFn is not a function or promise", nextFn);
}
}
else {
// console.log("default promise");
return this.then(nextFn);
}
}
答案 0 :(得分:14)
传递给Promise构造函数的回调始终是同步调用的,但传递给then
的回调总是异步调用(您可以在用户区中使用setTimeout
延迟0
实现那个)。
简化您的示例(并提供匿名函数的名称,以便我可以参考它们):
Promise.resolve().then(function callbackA () {
console.log("finish run 1");
}).then(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve().then(function callbackC () {
console.log("finish run 2");
})
仍然以相同的顺序给出输出:
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
事件按此顺序发生:
我能想到解决问题的最简单方法是使用具有Promise.prototype.isFulfilled功能的库,您可以使用它来决定是否同步调用第二个回调。例如:
var Promise = require( 'bluebird' );
Promise.prototype._SEPH_syncThen = function ( callback ) {
return (
this.isPending()
? this.then( callback )
: Promise.resolve( callback( this.value() ) )
);
}
Promise.resolve()._SEPH_syncThen(function callbackA () {
console.log("finish run 1");
})._SEPH_syncThen(function callbackB () {
console.log("surprisingly this happens after run 2 finishes");
});
Promise.resolve()._SEPH_syncThen(function callbackC () {
console.log("finish run 2");
})
输出:
finish run 1
surprisingly this happens after run 2 finishes
finish run 2
答案 1 :(得分:0)
您的代码很好,您希望您的承诺能够独立运行,并让它们以自己的方式执行,无论每个首先完成。一旦您的代码异步,您就无法预测哪一个将首先完成(由于event loop
的异步特性)。
但是,如果你想在完成所有承诺后抓住所有承诺,你应该使用Promise.all
(相当于$.when
是jQuery)。
见: