如何理解这个Promise执行顺序?

时间:2019-10-07 13:18:53

标签: javascript promise

我不明白为什么这段代码会产生这样的顺序?有人可以详细说明吗?我以为Promises就像一个FIFO队列,但是嵌套的Promise函数似乎有点不可预测,还是使用其他数据结构?

new Promise(resolve => {
    resolve()
  })
  .then(() => {
    new Promise(resolve => {
        resolve()
      })
      .then(() => {
        console.log(1)
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1)
    new Promise((resolve => {
        resolve()
      }))
      .then(() => {
        new Promise(resolve => {
            resolve()
          })
          .then(() => {
            console.log(4)
          })
          .then(() => {
            console.log(6)
          })
      }).then(() => {
        console.log(5)
      })
  }).then(() => {
    console.log(3)
  })
console.log(0)

输出:

0
1
1.1
2
3
3.1
4
5
6

3 个答案:

答案 0 :(得分:7)

承诺是异步的。这意味着每次创建新的Promise时,新的异步操作就会开始。

什么是JS中的异步操作?首先,您需要了解无论您做什么,JS都在单线程上运行。因此,为了使其看起来像异步的,有一个名为"event loop"的东西(将注释的链接链接到原始的帖子,tnx @Taki作了很好的参考)。

通常,事件循环在主代码动作之间的动作中存储所有异步功能和“清单”。这确实是过度简化的解释,请参阅链接以了解更多信息,但这就是要点。

因此,基本上,这里没有“ FIFO”队列-异步功能的顺序实际上取决于诸如处理器速度,操作系统等之类的东西。

但是-有一种方法可以确保仅在另一个异步操作完成后才执行一个异步操作,这就是.then子句。问题是,它仅确保.then内部的特定功能将在连接到其特定的诺言之后执行,但是它并没有说明它在其他异步操作(承诺)中的顺序。事件循环。例如,在您的代码中:

new Promise(resolve => {
    resolve() // PROMISE A
  })
  .then(() => {
    new Promise(resolve => {
        resolve() // PROMISE B
      })
      .then(() => {
        console.log(1) //PROMISE C
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1) // PROMISE D
    new Promise((resolve => {
        resolve()
      }))

我参加了其中的一部分来解释:

因此,承诺A首先解决。这保证了承诺B将立即解决。这是事情变得复杂的时候:由于承诺B已解决,因此承诺C和D现在都进入了事件循环!为什么?因为Promise A有2个.then子句,所以当第一个end-event循环采用第二个是promise D时,但是第一个.then子句中也有一个.then子句拥有-承诺C,它也会进入事件循环。

承诺D和C之间没有连接!它们可以以任何顺序执行。保持该逻辑,您将看到其余承诺的工作原理,并且如果尝试在不同的OS上运行它,则由于事件循环的OS的实现方式不同,承诺顺序可能会有所不同。

希望这对您有所了解。

免责声明 :我在JS方面没有太多经验,但是Promise确实吸引了我,因此我对此进行了深入研究。我站在我在这里写的所有内容的背后,但是如果我的解释有任何更正,我很想听听!

编辑

我下面的答案也是正确的,但没有任何解释,所以让我补充一下: 当您未在诺言(或.then子句中也返回诺言)中未返回任何内容时,它将在退出诺言之前隐式返回没有值的已解析诺言,基本上就像添加{{1 }}在return new Promise.resolve()之后的承诺C中。当这样完成时,所有在诺言B之后的console.log子句只会在前一个结束后才进入事件循环(例如b结束,因此C进入循环,然后进入下一个.then,依此类推),但是在它们之间也可以输入其他的Promise或.then子句(如Promise D)。

但是,当您返回链接有.then子句的诺言时,它确保了promise + then子句的整个块按顺序进入事件循环,所以{{1} }子句也将按照您想要的顺序执行:)

tnx @Eugene Sunic!

答案 1 :(得分:4)

由于代码中不存在返回值,结果顺序无法预测。

为您的承诺增加回报,您将获得可理解的输出,并且可以轻松跟踪承诺的执行情况。

首先,打印同步0,然后执行第一个promise块,就像您所说的FIFO。

1,2,3.1

此后,执行可链接的对象 1.1 之后,打印块4,6

遵循可输出的链接,输出5,最后,可输出的最后一个打印数字3

留下0,1,2,3.1,1.1,4,6,5,3

new Promise(resolve => resolve())
  .then(() => {
    return new Promise(resolve => resolve())
      .then(() => console.log(1))
      .then(() => console.log(2))
      .then(() => console.log(3.1));
  })
  .then(() => {
    console.log(1.1);
    return new Promise((resolve => resolve()))
      .then(() => {
        return new Promise((resolve) => resolve())
          .then(() => console.log(4))
          .then(() => console.log(6))
      }).then(() => console.log(5))
  }).then(() => console.log(3))

console.log(0)

答案 2 :(得分:0)

它是FIFO,执行如下:

main [4] logs: 0 // main code executed, one executor added to FIFO (4)
4 [8,18] // executor at line 4 runs, two executors added to FIFO (8, 18)
8 [18,11] logs: 1 // etc etc
18 [11,23,36] logs: 1.1
11 [23,36,14] logs: 2
23 [36,14,27,33] 
36 [14,27,33] logs: 3
14 [27,33] logs: 3.1
27 [33,30] logs: 4
33 [30] logs: 5
30 logs: 6

您可以看到它的先进先出顺序:[4,8,18,11,23,36,14,27,33,30],但它存储执行者(对已实现或拒绝的承诺的回调),而不是承诺。换句话说:履行或拒绝承诺的时间决定了将其添加到FIFO的时间,而不是创建承诺的时间。