【问题标题】:Resolve order of Promises within Promises解决 Promise 中 Promise 的顺序
【发布时间】:2017-06-21 05:26:08
【问题描述】:

下面的代码

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 resolve 在内部做什么?

【问题讨论】:

  • 按照您的编码方式,inner() 是 Promise 的一个孤立分支。你只知道它什么时候开始,但它不与任何东西协调。这通常是编写 Promise 的错误方式,甚至在某些 Promise 库中是一个警告,因为它通常是一个错误。通常,您会从 outer() 中返回 inner() 承诺,而不是让 outer() 创建自己的承诺。然后,一切都会协调起来。
  • 依赖独立promise链的顺序是一个糟糕的设计。您依赖于不受规范控制且永远不应该完成的特定承诺实现的实现细节。如果您关心两个单独的 Promise 链的顺序,那么您应该使用您的代码连接/控制它们,而不是猜测特定实现将如何运行。

标签: javascript promise


【解决方案1】:

简而言之,您会得到您所看到的行为,因为 inner() 承诺上的 .then() 方法在 outer() 承诺上的 .then() 方法之前首先运行,因此它的处理程序首先排队(请参阅步骤下面的步骤解释为什么会这样)。

Promise resolve 内部做什么?

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

我以为外部解析会是第一个进入 JS 消息的 队列后跟内部解析。但是 JS 事件循环会触发 首先是内部解析,然后是外部解析。

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

以下是对您的代码运行方式的一些分析以及可能的解释:

  1. 首先你打电话给outer()。这将创建一个 Promise 对象并同步调用您传递给它的 Promise 执行器回调。
  2. 该回调调用resolve(),它将排队调用任何当前附加的.then() 处理程序。请注意,在您调用resolve() 的那一刻,还没有.then() 处理程序,因为在此代码outer().then() 中,您仍在运行outer().then(),因为它还没有运行,所以没有实际上还没有任何东西要排队(这可能是您观察到的顺序的关键 - 请继续阅读以获取更多详细信息)。
  3. 然后,代码调用inner()。这会创建一个新的 Promise,然后(仍然同步运行)调用您传递到那里的 Promise 执行器回调,它调用 resolve()。同样,还没有附加任何 .then() 处理程序,因此仍然没有其他任何事情可以安排未来执行。
  4. 现在,inner() 内部的 Promise 执行器返回,.then() 方法在inner() 内部的该承诺上被调用。这个promise 已经被解决了,所以当这个.then() 处理程序被调用时,promise 知道安排它在未来运行。由于所有.then() 处理程序在堆栈仅展开到平台代码时被异步调用,因此它不会立即运行,而是通过将其放入队列中安排在将来运行。这个队列的工作方式(宏任务或微任务等)完全取决于实现,但我们知道 Promise 规范保证它在当前正在执行的同步 JS 代码完成运行并返回控制后运行返回系统。
  5. 现在inner() 返回(代码仍在同步运行)。
  6. 现在outer() 返回并且outer().then() 中的.then() 方法运行。就像在前面的例子中一样,当这个.then() 方法被调用时,宿主promise 已经被解析了。因此,promise 引擎将通过将 .then() 处理程序回调添加到队列中来安排其运行。
  7. 如果第 4 步和第 6 步中的这两个 .then() 处理程序按照它们运行的​​顺序排队(这将是逻辑实现),那么您会看到 inner() 上的 .then() 处理程序首先运行,然后outer() 上的 .then() 处理程序将从 inner().then() ran first beforeouter().then()` 开始运行。这就是你观察到的。
  8. 尽管outer()inner() 之前被解析,但在outer() 被解析时,没有附加.then() 处理程序,因此当它被解析时,没有什么可以安排未来执行。这可能就是为什么即使它首先被解决,它的.then() 处理程序也不会首先运行。一旦inner()outer() 都被解析,首先运行的是inner 的.then() 方法,因此它首先会调度.then() 处理程序运行,这就是您观察到的。

您可以通过阅读和研究这些参考资料来了解正在发生的事情的一些额外背景:

What is the order of execution in javascript promises

Difference between microtask and macrotask within an event loop context.


如果您想更明确地指定内部 .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() 处理程序中启动它,如下所示:

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

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

【讨论】:

    【解决方案2】:

    我认为外部解析将首先进入 JS 消息队列,然后是内部解析。

    是的,首先解决了“外部”承诺。在resolve 呼叫旁边放一个console.log
    但是不,外部 then 回调不会首先放入队列中,因为它是在内部 then 回调之后安装的。你所做的基本上相当于

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-06
      • 2021-04-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      相关资源
      最近更新 更多