在Promises中解决Promise的顺序

时间:2017-02-03 18:47:24

标签: javascript promise

以下代码

function inner () {
  new Promise(function(resolve,reject){
    resolve()
  }).then(function(){
    console.log('Inner Promise')
  })
}
function outer() {
  return new Promise(function(resolve, reject){
    resolve()
    inner()
  })
}

outer().then(function(data) {
  console.log('Outer Promise')
})

输出

Inner Promise
Outer Promise

我认为外部决心将是第一个进入JS消息队列,然后是内部解析。 然而,JS事件循环首先触发内部解析,然后是外部解析。

Promise在内部解决了什么?

2 个答案:

答案 0 :(得分:6)

简而言之,您获得了所看到的行为,因为.then()承诺上的inner()方法首先在.then()承诺上的outer()方法之前运行,因此它& #39; s处理程序首先排队(请参阅下面的分步说明,了解其原因)。

  

Promise在内部解决了什么?

resolve()将承诺的内部状态更改为已实现。此时,如果已经附加到promise的任何.then()处理程序,它们将被添加到队列中,以便在堆栈展开并且Javascript的当前运行路径完成并将控制权返回给系统时执行。请注意,正如您将在本案例中看到的那样(当您阅读下面的逐步分析时),如果还没有任何已注册的.then()处理程序,则无法将任何内容添加到队列中。

  

我认为外部决心将是第一个进入JS消息   队列之后是内在的决心。但是JS Event Loop会触发   首先是内心的决心,然后是外面的决心。

Promise解决操作未添加到队列中。 resolve()是同步的。它立即将当前承诺的状态更改为Fulfilled状态。如果在解析promise时,已经注册了任何.then()个处理程序,那么它们就是添加到队列中的内容。但是,在你的承诺中,在你的每一个承诺得到解决的那一刻,还没有附加.then()个处理程序。因此,那些.then()处理程序在承诺解决时不会排队。相反,当.then()方法实际运行并注册它们时,它们将在稍后排队。

这里有一些关于代码如何运行的分析以及可能的解释:

  1. 首先致电outer()。这将创建一个Promise对象并同步调用您传递它的promise executor回调。
  2. 该回调调用resolve(),它将排队调用任何当前连接的.then()处理程序。请注意,在您拨打resolve()时,尚未设置.then()个处理程序,因为在此代码outer().then()中,您仍在运行outer()和{{ 1}}之后还没有运行,所以实际上还没有排队的东西(这可能是你观察到的顺序的关键 - 请继续阅读以获取更多细节)。
  3. 然后,代码调用.then()。这会创建一个新的promise,然后(仍然同步运行)调用你在那里调用inner()的promise执行者回调。同样,还没有附加任何resolve()个处理程序,因此仍然没有其他任何可以安排以供将来执行。
  4. 现在,.then()内的Promise执行程序返回,并在inner()内的该承诺上调用.then()方法。此承诺已经解决,因此,当调用此inner()处理程序时,承诺知道将其安排在将来运行。由于当堆栈仅展开到平台代码时,所有.then()处理程序都是异步调用的,因此它不会立即运行,而是计划在将来通过将其放入队列来运行。它的实现依赖于这个队列的工作原理(宏任务或微任务等等),但是我们知道Promise规范可以保证它在执行的当前同步JS代码之后运行并完成运行并返回控制回到系统。
  5. 现在.then()返回(代码仍在同步运行)。
  6. 现在inner()返回,outer()中的.then()方法运行。与前面的示例一样,当调用此outer().then()方法时,主机承诺已经解决。因此,promise引擎将通过将.then()处理程序回调添加到队列来安排运行.then()处理程序回调。
  7. 如果步骤4和6中的这两个.then()处理程序按照它们运行的​​顺序排队(这将是逻辑实现),那么您将在{{1}上看到.then()处理程序首先运行,然后inner()上的.then()处理程序将从outer() outer()。then()`开始运行。这就是你观察到的。
  8. 即使在inner().then() ran first before之前已解决outer(),但在解析inner()时,仍未附加outer()个处理程序,因此没有任何内容可供日后执行何时解决。这可能是为什么即使它首先被解决,它的.then()处理程序也不会先运行。一旦.then()inner()都得到解决,它就会首先运行内部outer()方法,因此它会在安排.then()处理程序运行时获得第一次破解,这就是你观察到的。
  9. 通过阅读和研究这些参考资料,您可以获得一些额外的背景信息:

    What is the order of execution in javascript promises

    Difference between microtask and macrotask within an event loop context

    如果您想更明确地指定内部.then()处理程序首先触发,您可以简单地将其链接到.then()承诺,如下所示:

    
    
    outer()
    
    
    

    如果您想保证首先调用function inner () { return new Promise(function(resolve,reject){ resolve(); }).then(function(){ console.log('Inner Promise') }) } function outer() { // Add return here to chain the inner promise // make to make sure that outer() does not resolve until // inner() is completely done return inner(); } outer().then(function(data) { console.log('Outer Promise') })处理程序,则必须选择不同的结构,因为此结构不会以任何方式强制执行此类订单,并且不能除非你有意识地延迟outer().then()的运行(使用inner()或某些此类事情)或重组代码,否则不要被这个方向哄骗。例如,如果您真的想要重组以强制setTimeout()最后运行,那么您可以在inner()处理程序中将其启动,如下所示:

    
    
    outer().then()
    
    
    

答案 1 :(得分:1)

  

我认为外部决心将是第一个进入JS Message Queue,然后是内部决心。

是的,首先解决“外部”承诺。在console.log电话旁边放resolve 但不是,外部然后回调没有首先放入队列,因为它安装在内部然后回调之后。你在做什么基本上等同于

var outer = Promise.resolve();
var inner = Promise.resolve();
inner.then(function() {
    console.log('Inner Promise')
});
outer.then(function(data) {
    console.log('Outer Promise')
});

但由于嵌套(同步)函数调用而混淆。