为什么javascript承诺在只调用同步函数时是异步的?

为什么javascript承诺在只调用同步函数时是异步的?,javascript,asynchronous,promise,synchronous,Javascript,Asynchronous,Promise,Synchronous,在测试中,我发现JavaScript承诺总是异步的,不管它们的链中是否包含任何异步函数 下面是一些显示控制台中操作顺序的代码。如果您运行它,您将看到,即使每个函数都是同步的,输出也会显示两个aPromise()调用并行运行,并且“这意外地发生在运行2完成之后”不会发生在运行2完成之前 函数aPromise(){ 返回新承诺(功能(解决、拒绝){ console.log(“做出承诺A”) 解析(bPromise()); console.log(“承诺A已解决”) }); } 函数bPromise

在测试中,我发现JavaScript承诺总是异步的,不管它们的链中是否包含任何异步函数

下面是一些显示控制台中操作顺序的代码。如果您运行它,您将看到,即使每个函数都是同步的,输出也会显示两个
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
事件按以下顺序发生:

  • 第一个承诺已(同步)解决
  • callbackA被添加到事件循环的队列中
  • 第二个承诺已经解决
  • callbackC被添加到事件循环的队列中
  • 这样就可以访问事件循环了,callbackA是队列中的第一个,所以它被执行,它不返回承诺,所以callbackB的中间承诺立即被同步解析,从而将callbackB附加到事件循环的队列中
  • 没有什么可以做的了,所以事件循环被访问,callbackC是队列中的第一个,所以它被执行
  • 这样就可以访问事件循环了,callbackB是队列中的第一个,因此它被执行
  • 我能想到的解决问题的最简单方法是使用一个库,该库具有一个函数,您可以使用该函数来决定是否同步调用第二个回调。例如:

    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