为什么javascript承诺在只调用同步函数时是异步的?
在测试中,我发现JavaScript承诺总是异步的,不管它们的链中是否包含任何异步函数 下面是一些显示控制台中操作顺序的代码。如果您运行它,您将看到,即使每个函数都是同步的,输出也会显示两个为什么javascript承诺在只调用同步函数时是异步的?,javascript,asynchronous,promise,synchronous,Javascript,Asynchronous,Promise,Synchronous,在测试中,我发现JavaScript承诺总是异步的,不管它们的链中是否包含任何异步函数 下面是一些显示控制台中操作顺序的代码。如果您运行它,您将看到,即使每个函数都是同步的,输出也会显示两个aPromise()调用并行运行,并且“这意外地发生在运行2完成之后”不会发生在运行2完成之前 函数aPromise(){ 返回新承诺(功能(解决、拒绝){ console.log(“做出承诺A”) 解析(bPromise()); console.log(“承诺A已解决”) }); } 函数bPromise
aPromise()
调用并行运行,并且“这意外地发生在运行2完成之后”
不会发生在运行2完成之前
函数aPromise(){
返回新承诺(功能(解决、拒绝){
console.log(“做出承诺A”)
解析(bPromise());
console.log(“承诺A已解决”)
});
}
函数bPromise(){
返回新承诺(功能(解决、拒绝){
console.log(“制定和解决承诺B”)
解决();
});
}
aPromise().then(function()){
控制台日志(“完成运行1”);
}).然后(函数(){
log(“令人惊讶的是,这发生在运行2完成之后”);
});
aPromise().then(function()){
控制台日志(“完成运行2”);
})
传递给承诺构造函数的回调始终是同步调用的,但传递到然后
的回调始终是异步调用的(可以在userland实现中使用延迟为0
的setTimeout
来实现这一点)
将示例简化为(并给出匿名函数的名称,以便我可以引用它们):
仍然以相同的顺序给出输出:
finish run 1
finish run 2
surprisingly this happens after run 2 finishes
事件按以下顺序发生:
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
您的代码很好,因为您希望您的承诺能够独立运行,并让它们以自己的方式执行,而不管每个承诺是否首先完成。一旦代码是异步的,就无法预测将首先完成哪一个(由于
事件循环的异步性质)
但是,如果您希望在所有承诺都完成后捕获它们,则应使用Promise.all
(这相当于$。当是jQuery时)。
见:
其他人可能会挖掘出具体的讨论,但总体理念是一致性/可预测性是界面设计的一个优点。一些早期的Promise实现并不是这样的,但随着人们被意想不到的bug所困扰,人们的情绪发生了变化。一般来说,如果你知道承诺总是异步处理的,那么你就更容易对其进行推理。你知道什么样的bug吗?快速看一看,上面的措辞是“将立即将要调用的作业排队”,排队的对象最快可以调用的时间是在当前函数结束后<代码>新承诺(r=>r()),然后(()=>console.log('yay');设i=10;而(--i)console.log(i)代码>日志9..1在yay@SephReed:那种getPromise().then(函数(x){foo.bar(x);});var foo=new foo()代码>错误。它可以很好地与A+承诺配合使用,但在之后
意外地同步调用其回调时就不行了。请看一看事件队列,它使承诺始终异步运行。这是有道理的。你知道为什么选择这个设计吗?“这对我来说有点违反直觉。”简短的回答是:一致性。详细回答:质疑语言开发人员的设计选择超出了堆栈溢出的范围。@sephered我相信这样就有了一个标准接口/API,无论执行什么代码,它都将始终具有标准行为。根据应用程序改变其功能可能会导致意外行为、复杂性和混乱。@Pualpro非常感谢您的帮助。我真的希望找到一种方法来停止使用我自制的承诺系统(尽管我喜欢),这似乎是一个完美的解决方案。我真的很感激。这种变通方法太可怕了。它不捕捉错误,它颠覆了承诺的设计,如果你用异步解析的承诺启动你的链,它甚至不起作用。OP问题的正确解决方案是使用然后显式链接运行2,等待运行1完成。
finish run 1
surprisingly this happens after run 2 finishes
finish run 2