JavaScript Promise 的执行顺序是什么? [英] What is the order of execution in JavaScript promises?

查看:27
本文介绍了JavaScript Promise 的执行顺序是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解以下使用 JavaScript 承诺的代码段的执行顺序.

Promise.resolve('A').then(function(a){console.log(2, a); return 'B';}).then(函数(a){Promise.resolve('C').then(function(a){console.log(7, a);}).then(function(a){console.log(8, a);});console.log(3, a);返回一个;}).then(函数(a){Promise.resolve('D').then(function(a){console.log(9, a);}).then(function(a){console.log(10, a);});console.log(4, a);}).then(函数(a){console.log(5, a);});控制台日志(1);setTimeout(function(){console.log(6)},0);

结果是:

12 A"3B"7 C"4 B"8 未定义9 D"5 未定义10 未定义6

我对执行顺序感到好奇 1 2 3 7... 不是值 "A", "B"...

我的理解是,如果承诺被解析,then 函数会被放入浏览器事件队列中.所以我的期望是 1 2 3 4 ...

为什么不是 1 2 3 4 ... 记录的顺序?

解决方案

评论

首先,在 .then() 处理程序中运行 Promise 并且不从 .then() 回调中返回这些 Promise 会创建一个全新的未附加的 Promise 序列不以任何方式与父承诺同步.通常,这是一个错误,实际上,当您这样做时,一些承诺引擎实际上会发出警告,因为这几乎不是您想要的行为.唯一想要这样做的时候是当你在做某种即发即忘"的操作时,你不关心错误,也不关心与世界其他地方的同步.

因此,.then() 处理程序中的所有 Promise.resolve() 承诺都会创建新的 Promise 链,这些链独立于父链运行.对于实际的异步操作,对于非连接的、独立的承诺链,您没有确定的行为.这有点像并行启动四个 ajax 调用.你不知道哪一个会先完成.现在,由于那些 Promise.resolve() 处理程序中的所有代码恰好是同步的(因为这不是真实世界的代码),那么您可能会获得一致的行为,但这不是设计承诺点,所以我不会花太多时间试图找出哪个仅运行同步代码的 Promise 链将首先完成.在现实世界中,这并不重要,因为如果顺序很重要,那么您就不会以这种方式让事情发生.

总结

  1. 所有 .then() 处理程序在当前执行线程完成后异步调用(如 Promises/A+ 规范所说,当 JS 引擎返回到平台代码"时)).即使对于同步解析的 Promise 也是如此,例如 Promise.resolve().then(...).这样做是为了编程一致性,以便 .then() 处理程序始终异步调用,无论 promise 是立即解决还是稍后解决.这可以防止一些计时错误,并使调用代码更容易看到一致的异步执行.

  2. 如果 setTimeout() 和已调度的 .then() 处理程序都已排队并准备好运行,则没有规范来确定它们的相对顺序.在您的实现中,挂起的 .then() 处理程序总是在挂起的 setTimeout() 之前运行,但 Promises/A+ 规范规范说这不是确定的.它说 .then() 处理程序可以通过多种方式进行调度,其中一些将在挂起的 setTimeout() 调用之前运行,而其中一些可能在挂起之后运行setTimeout() 调用.例如,Promises/A+ 规范允许使用 setImmediate() 调度 .then() 处理程序,它会在挂起 setTimeout() 之前运行> 调用或使用 setTimeout() 将在挂起的 setTimeout() 调用之后运行.因此,您的代码根本不应依赖于该顺序.

  3. 多个独立的 Promise 链没有可预测的执行顺序,您不能依赖任何特定的顺序.这就像并行触发四个 ajax 调用,而您不知道哪个会先完成.

  4. 如果执行顺序很重要,请不要创建依赖于细节实施细节的竞赛.相反,链接承诺链以强制执行特定的执行顺序.

  5. 您通常不想在 .then() 处理程序中创建不从处理程序返回的独立承诺链.这通常是一个错误,除非在极少数情况下发生火灾和忘记而没有错误处理.

逐行分析

那么,这是对您的代码的分析.我添加了行号并清理了缩进以便于讨论:

1 Promise.resolve('A').then(function (a) {2 console.log(2, a);3 返回'B';4 }).then(function (a) {5 Promise.resolve('C').then(function (a) {6 console.log(7, a);7 }).then(函数(a){8 console.log(8, a);9 });10 console.log(3, a);11 返回一个;12 }).then(函数(a){13 Promise.resolve('D').then(function (a) {14 console.log(9, a);15 }).then(函数(a){16 控制台日志(10,一);17 });18 console.log(4, a);19 }).then(函数(a){20 console.log(5, a);21 });2223 控制台日志(1);2425 setTimeout(函数(){26 控制台日志(6)27 }, 0);

第 1 行 启动了一个承诺链,并为其附加了一个 .then() 处理程序.由于 Promise.resolve() 立即解析,Promise 库将安排第一个 .then() 处理程序在此 Javascript 线程完成后运行.在 Promises/A+ 兼容的 Promise 库中,所有 .then() 处理程序在当前执行线程完成后和 JS 返回到事件循环时异步调用.这意味着该线程中的任何其他同步代码(例如您的 console.log(1) 将在接下来运行,这就是您所看到的.

顶层(第 4、12、19 行)的所有其他 .then() 处理程序链接在第一个之后,并且只会在第一个之后运行轮到它了.他们基本上在这一点上排队.

由于 setTimeout() 也在这个初始执行线程中,所以它会运行并因此调度一个计时器.

即同步执行结束.现在,JS 引擎开始运行事件队列中安排的内容.

据我所知,不能保证首先是 setTimeout(fn, 0).then() 处理程序,它们都计划运行在这个执行线程之后..then() 处理程序被认为是微任务";所以它们在 setTimeout() 之前首先运行并不让我感到惊讶.但是,如果您需要一个特定的顺序,那么您应该编写保证顺序的代码,而不是依赖于这个实现细节.

无论如何,第 1 行 上定义的 .then() 处理程序接下来会运行.因此,您会看到 console.log(2, a) 的输出 2 "A".

接下来,由于之前的 .then() 处理程序返回了一个普通值,该承诺被视为已解决,因此 .then() 处理程序定义在 行4 运行.在这里,您将创建另一个独立的承诺链并引入通常是错误的行为.

第 5 行,创建一个新的 Promise 链.它解析最初的承诺,然后安排两个 .then() 处理程序在当前执行线程完成时运行.在当前的执行线程中是第 10 行的 console.log(3, a) 所以这就是你接下来看到的原因.然后,这个执行线程结束,它返回到调度程序,看看接下来要运行什么.

我们现在有几个 .then() 处理程序在队列中等待下一次运行.我们刚刚在第 5 行安排了一个,在第 12 行的更高级别链中还有下一个.如果您在第 5 行这样做了:

return Promise.resolve.then(...)

然后您将这些承诺联系在一起,并按顺序进行协调.但是,通过不返回承诺值,您启动了一个全新的承诺链,该链与外部更高级别的承诺不协调.在您的特定情况下,promise 调度程序决定接下来运行嵌套更深的 .then() 处理程序.老实说,我不知道这是按照规范、约定还是只是一个 promise 引擎与另一个的实现细节.我想说的是,如果顺序对你很重要,那么你应该通过将承诺链接到特定顺序来强制执行命令,而不是依赖于谁先赢得比赛.

无论如何,就您而言,这是一场调度竞赛,您正在运行的引擎决定运行在接下来的第 5 行定义的内部 .then() 处理程序,因此您会看到 7 在第 6 行 指定的C".然后它什么都不返回,所以这个 promise 的解析值变成 undefined.

返回调度程序,它在第 12 行上运行 .then() 处理程序.这又是 .then() 处理程序和 第 7 行 上的处理程序之间的竞争,后者也在等待运行.我不知道为什么它在这里选择一个而不是另一个,只是说它可能是不确定的或每个承诺引擎会有所不同,因为代码没有指定顺序.在任何情况下,第 12 行 中的 .then() 处理程序开始运行.这再次创建了一个新的独立或不同步的承诺链.它再次调度 .then() 处理程序,然后您从 .then() 中的同步代码中获得 4 B"处理程序.所有同步代码都在该处理程序中完成,所以现在,它返回到下一个任务的调度程序.

返回调度程序,它决定在第 7 行 上运行 .then() 处理程序,您会得到 8 undefined.承诺有 undefined 因为该链中之前的 .then() 处理程序没有返回任何东西,因此它的返回值为 undefined,因此这是当时承诺链的解析值.

此时,输出为:

12 A"3B"7 C"4 B"8 未定义

同样,所有同步代码都已完成,因此它再次返回到调度程序,并决定运行在第 13 行 上定义的 .then() 处理程序.运行后你会得到输出 9 "D" 然后它再次返回到调度程序.

与之前嵌套的 Promise.resolve() 链一致,调度选择运行在 行 19 上定义的下一个外部 .then() 处理程序.它运行并得到输出 5 undefined.它又是 undefined 因为该链中之前的 .then() 处理程序没有返回值,因此 promise 的解析值是 undefined.

到此为止,输出是:

12 A"3B"7 C"4 B"8 未定义9 D"5 未定义

此时,只有一个 .then() 处理程序计划运行,因此它运行在第 15 行 上定义的处理程序,您将获得输出 10 个未定义 下一个.

然后,最后,setTimeout() 开始运行,最终输出是:

12 A"3B"7 C"4 B"8 未定义9 D"5 未定义10 未定义6

如果要尝试准确预测这将运行的顺序,那么将有两个主要问题.

  1. 待处理的 .then() 处理程序与同样待处理的 setTimeout() 调用的优先级如何.

  2. promise 引擎如何决定优先处理多个等待运行的 .then() 处理程序.根据您使用此代码的结果,它不是 FIFO.

对于第一个问题,我不知道这是根据规范还是只是在 promise 引擎/JS 引擎中的实现选择,但是您报告的实现似乎优先考虑所有待处理的 .then() 任何 setTimeout() 调用之前的处理程序.您的情况有点奇怪,因为除了指定 .then() 处理程序之外,您没有实际的异步 API 调用.如果您有任何异步操作实际上在此承诺链的开头需要任何时间来执行,那么您的 setTimeout() 将在 .then() 处理程序之前执行在真正的异步操作上只是因为真正的异步操作需要实际时间来执行.所以,这只是一个人为的例子,并不是真实代码的常见设计案例.

对于第二个问题,我看到了一些讨论,讨论了如何优先处理处于不同嵌套级别的待处理 .then() 处理程序.我不知道该讨论是否在规范中得到解决.我更喜欢以一种对我来说无关紧要的方式进行编码.如果我关心我的异步操作的顺序,那么我会链接我的承诺链来控制顺序,这个级别的实现细节不会以任何方式影响我.如果我不关心顺序,那么我也不关心顺序,因此实现细节的级别不会影响我.即使这是在某些规范中,这似乎是不应该在许多不同的实现(不同的浏览器,不同的承诺引擎)中信任的细节类型,除非您在要运行的任何地方都对其进行了测试.因此,当您有不同步的承诺链时,我建议不要依赖特定的执行顺序.


您可以通过像这样链接所有的承诺链来使订单 100% 确定(返回内部承诺,以便它们链接到父链中):

Promise.resolve('A').then(function (a) {console.log(2, a);返回'B';}).then(函数(a){var p = Promise.resolve('C').then(function (a) {console.log(7, a);}).then(函数(a){console.log(8, a);});console.log(3, a);//返回这个承诺以链接到父承诺返回 p;}).then(函数(a){var p = Promise.resolve('D').then(function (a) {console.log(9, a);}).then(函数(a){控制台日志(10,一);});console.log(4, a);//返回这个承诺以链接到父承诺返回 p;}).then(函数(a){console.log(5, a);});控制台日志(1);设置超时(函数(){控制台日志(6)}, 0);

这在 Chrome 中给出了以下输出:

12 A"3B"7 C"8 未定义4 未定义9 D"10 未定义5 未定义6

而且,由于承诺已经全部链接在一起,承诺顺序全部由代码定义.唯一剩下的实现细节是 setTimeout() 的时间,在你的例子中,它是最后一个,在所有挂起的 .then() 处理程序之后.

在检查 Promises/A+ 规范后,我们发现:<块引用>

2.2.4 在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilled 或 onRejected.[3.1].

....

3.1 这里的平台代码"是指引擎、环境和promise实现代码.在实践中,这一要求确保onFulfilled 和 onRejected 异步执行,在事件之后循环在其中调用 then,并使用新堆栈.这可以使用宏任务"机制实现,例如 setTimeout 或setImmediate,或使用微任务"机制,例如MutationObserver 或 process.nextTick.自从承诺执行被认为是平台代码,它本身可能包含一个任务调度调用处理程序的队列或蹦床".

这表示 .then() 处理程序必须在调用堆栈返回到平台代码后异步执行,但将它完全留给实现,无论它是用宏任务完成的,如何准确地做到这一点像 setTimeout() 或像 process.nextTick() 这样的微任务.因此,根据本规范,它不是确定的,不应依赖.

我在 ES6 规范中没有找到关于宏任务、微任务或承诺 .then() 处理程序与 setTimeout() 时间的信息.这也许并不奇怪,因为 setTimeout() 本身不是 ES6 规范的一部分(它是一个宿主环境函数,而不是一个语言特性).

我还没有找到任何支持这一点的规范,但是这个问题的答案事件循环上下文中微任务和宏任务的区别 解释了在具有宏任务和微任务的浏览器中的工作方式.

仅供参考,如果您想了解更多关于微任务和宏任务的信息,这里有一篇关于该主题的有趣参考文章:任务、微任务、队列和计划.

I'd like to understand the execution order of the following snippet that uses JavaScript promises.

Promise.resolve('A')
  .then(function(a){console.log(2, a); return 'B';})
  .then(function(a){
     Promise.resolve('C')
       .then(function(a){console.log(7, a);})
       .then(function(a){console.log(8, a);});
     console.log(3, a);
     return a;})
  .then(function(a){
     Promise.resolve('D')
       .then(function(a){console.log(9, a);})
       .then(function(a){console.log(10, a);});
     console.log(4, a);})
  .then(function(a){
     console.log(5, a);});
console.log(1);
setTimeout(function(){console.log(6)},0);

The result is:

1
2 "A"
3 "B"
7 "C"
4 "B"
8 undefined
9 "D"
5 undefined
10 undefined
6

I'm curious about the execution order 1 2 3 7... not the values "A", "B"...

My understanding is that if a promise is resolved the then function is put in the browser event queue. So my expectation was 1 2 3 4 ...

Why isn't 1 2 3 4 ... the logged order?

解决方案

Comments

First off, running promises inside of a .then() handler and NOT returning those promises from the .then() callback creates a completely new unattached promise sequence that is not synchronized with the parent promises in any way. Usually, this is a bug and, in fact, some promise engines actually warn when you do that because it is almost never the desired behavior. The only time one would ever want to do that is when you're doing some sort of fire and forget operation where you don't care about errors and you don't care about synchronizing with the rest of the world.

So, all your Promise.resolve() promises inside of .then() handlers create new Promise chains that run independently of the parent chain. With actual asynchronous operations, you do not have a determinate behavior with non-connected, independent promise chains. It's kind of like launching four ajax calls in parallel. You don't know which one will complete first. Now, since all your code inside those Promise.resolve() handlers happens to be synchronous (since this isn't real world code), then you might get consistent behavior, but that isn't the design point of promises so I wouldn't spend much time trying to figure out which Promise chain that runs synchronous code only is going to finish first. In the real world, it doesn't matter because if order matters, then you won't leave things to chance this way.

Summary

  1. All .then() handlers are called asynchronously after the current thread of execution finishes (as the Promises/A+ spec says, when the JS engine returns back to "platform code"). This is true even for promises that are resolved synchronously such as Promise.resolve().then(...). This is done for programming consistency so that a .then() handler is consistently called asynchronously no matter whether the promise is resolved immediately or later. This prevents some timing bugs and makes it easier for the calling code to see consistent asynchronous execution.

  2. There is no specification that determines the relative order of setTimeout() vs. scheduled .then() handlers if both are queued and ready to run. In your implementation, a pending .then() handler is always run before a pending setTimeout(), but the Promises/A+ spec specification says this is not determinate. It says that .then() handlers can be scheduled a whole bunch of ways, some of which would run before pending setTimeout() calls and some of which might run after pending setTimeout() calls. For example, the Promises/A+ spec allows .then() handlers to be scheduled with either setImmediate() which would run before pending setTimeout() calls or with setTimeout() which would run after pending setTimeout() calls. So, your code should not depend upon that order at all.

  3. Multiple independent Promise chains do not have a predictable order of execution and you cannot rely on any particular order. It's like firing off four ajax calls in parallel where you don't know which one will complete first.

  4. If order of execution is important, do not create a race that is dependent upon minute implementation details. Instead, link promise chains to force a particular execution order.

  5. You generally do not want to create independent promise chains within a .then() handler that are not returned from the handler. This is usually a bug except in rare cases of fire and forget without error handling.

Line By Line Analsysis

So, here's an analysis of your code. I added line numbers and cleaned up the indentation to make it easier to discuss:

1     Promise.resolve('A').then(function (a) {
2         console.log(2, a);
3         return 'B';
4     }).then(function (a) {
5         Promise.resolve('C').then(function (a) {
6             console.log(7, a);
7         }).then(function (a) {
8             console.log(8, a);
9         });
10        console.log(3, a);
11        return a;
12    }).then(function (a) {
13        Promise.resolve('D').then(function (a) {
14            console.log(9, a);
15        }).then(function (a) {
16            console.log(10, a);
17        });
18        console.log(4, a);
19    }).then(function (a) {
20        console.log(5, a);
21    });
22   
23    console.log(1);
24    
25    setTimeout(function () {
26        console.log(6)
27    }, 0);

Line 1 starts a promise chain and attached a .then() handler to it. Since Promise.resolve() resolves immediately, the Promise library will schedule the first .then() handler to run after this thread of Javascript finishes. In Promises/A+ compatible promise libraries, all .then() handlers are called asynchronously after the current thread of execution finishes and when JS goes back to the event loop. This means that any other synchronous code in this thread such as your console.log(1) will run next which is what you see.

All the other .then() handlers at the top level (lines 4, 12, 19) chain after the first one and will run only after the first one gets its turn. They are essentially queued at this point.

Since the setTimeout() is also in this initial thread of execution, it is run and thus a timer is scheduled.

That is the end of the synchronous execution. Now, the JS engine starts running things that are scheduled in the event queue.

As far as I know, there is no guarantee which comes first a setTimeout(fn, 0) or a .then() handler that are both scheduled to run right after this thread of execution. .then() handlers are considered "micro-tasks" so it does not surprise me that they run first before the setTimeout(). But, if you need a particular order, then you should write code that guarantees an order rather than rely on this implementation detail.

Anyway, the .then() handler defined on line 1 runs next. Thus you see the output 2 "A" from that console.log(2, a).

Next, since the previous .then() handler returned a plain value, that promise is considered resolved so the .then() handler defined on line 4 runs. Here's where you're creating another independent promise chain and introducing a behavior that is usually a bug.

Line 5, creates a new Promise chain. It resolves that initial promise and then schedules two .then() handlers to run when the current thread of execution is done. In that current thread of execution is the console.log(3, a) on line 10 so that's why you see that next. Then, this thread of execution finishes and it goes back to the scheduler to see what to run next.

We now have several .then() handlers in the queue waiting to run next. There's the one we just scheduled on line 5 and there's the next one in the higher level chain on line 12. If you had done this on line 5:

return Promise.resolve.then(...)

then you would have linked these promises together and they would be coordinated in sequence. But, by not returning the promise value, you started a whole new promise chain that is not coordinated with the outer, higher level promise. In your particular case, the promise scheduler decides to run the more deeply nested .then() handler next. I don't honestly know if this is by specification, by convention or just an implementation detail of one promise engine vs. the other. I'd say that if the order is critical to you, then you should force an order by linking promises in a specific order rather than rely on who wins the race to run first.

Anyway, in your case, it's a scheduling race and the engine you are running decides to run the inner .then() handler that's defined on line 5 next and thus you see the 7 "C" specified on line 6. It then returns nothing so the resolved value of this promise becomes undefined.

Back in the scheduler, it runs the .then() handler on line 12. This is again a race between that .then() handler and the one on line 7 which is also waiting to run. I don't know why it picks one over the other here other than to say it may be indeterminate or vary per promise engine because the order is not specified by the code. In any case, the .then() handler in line 12 starts to run. That again creates a new independent or unsynchronized promise chain line the previous one. It schedules a .then() handler again and then you get the 4 "B" from the synchronous code in that .then() handler. All synchronous code is done in that handler so now, it goes back to the scheduler for the next task.

Back in the scheduler, it decides to run the .then() handler on line 7 and you get 8 undefined. The promise there is undefined because the previous .then() handler in that chain did not return anything, thus its return value was undefined, thus that is the resolved value of the promise chain at that point.

At this point, the output so far is:

1
2 "A"
3 "B"
7 "C"
4 "B"
8 undefined

Again, all synchronous code is done so it goes back to the scheduler again and it decides to run the .then() handler defined on line 13. That runs and you get the output 9 "D" and then it goes back to the scheduler again.

Consistent with the previously nested Promise.resolve() chain, the the schedule chooses to run the next outer .then() handler defined on line 19. It runs and you get the output 5 undefined. It is again undefined because the previous .then() handler in that chain did not return a value, thus the resolved value of the promise was undefined.

As this point, the output so far is:

1
2 "A"
3 "B"
7 "C"
4 "B"
8 undefined
9 "D"
5 undefined

At this point, there is only one .then() handler scheduled to be run so it runs the one defined on line 15 and you get the output 10 undefined next.

Then, lastly, the setTimeout() gets to run and the final output is:

1
2 "A"
3 "B"
7 "C"
4 "B"
8 undefined
9 "D"
5 undefined
10 undefined
6

If one were to try to predict exactly the order this would run in, then there would be two main questions.

  1. How are pending .then() handlers prioritized vs. setTimeout() calls that are also pending.

  2. How does the promise engine decide to prioritize multiple .then() handlers that are all waiting to run. Per your results with this code it is not FIFO.

For the first question, I don't know if this is per specification or just an implementation choice here in the promise engine/JS engine, but the implementation you reported on appears to prioritize all pending .then() handlers before any setTimeout() calls. Your case is a bit of an odd one because you have no actual async API calls other than specifying .then() handlers. If you had any async operation that actually took any real time to execute at the start of this promise chain, then your setTimeout() would execute before the .then() handler on the real async operation just because the real async operation takes actual time to execute. So, this is a bit of a contrived example and is not the usual design case for real code.

For the second question, I've seen some discussion that discusses how pending .then() handlers at different levels of nesting should be prioritized. I don't know if that discussion was ever resolved in a specification or not. I prefer to code in a way that that level of detail does not matter to me. If I care about the order of my async operations, then I link my promise chains to control the order and this level of implementation detail does not affect me in any way. If I don't care about the order, then I don't care about the order so again that level of implementation detail does not affect me. Even if this was in some specification, it seems like the type of detail that should not be trusted across many different implementations (different browsers, different promise engines) unless you had tested it everywhere you were going to run. So, I'd recommend not relying on a specific order of execution when you have unsynchronized promise chains.


You could make the order 100% determinate by just linking all your promise chains like this (returning inner promises so they are linked into the parent chain):

Promise.resolve('A').then(function (a) {
    console.log(2, a);
    return 'B';
}).then(function (a) {
    var p =  Promise.resolve('C').then(function (a) {
        console.log(7, a);
    }).then(function (a) {
        console.log(8, a);
    });
    console.log(3, a);
    // return this promise to chain to the parent promise
    return p;
}).then(function (a) {
    var p = Promise.resolve('D').then(function (a) {
        console.log(9, a);
    }).then(function (a) {
        console.log(10, a);
    });
    console.log(4, a);
    // return this promise to chain to the parent promise
    return p;
}).then(function (a) {
    console.log(5, a);
});

console.log(1);

setTimeout(function () {
    console.log(6)
}, 0);

This gives the following output in Chrome:

1
2 "A"
3 "B"
7 "C"
8 undefined
4 undefined
9 "D"
10 undefined
5 undefined
6

And, since the promise have all been chained together, the promise order is all defined by the code. The only thing left as an implementation detail is the timing of the setTimeout() which, as in your example, comes last, after all pending .then() handlers.

Edit:

Upon examination of the Promises/A+ specification, we find this:

2.2.4 onFulfilled or onRejected must not be called until the execution context stack contains only platform code. [3.1].

....

3.1 Here "platform code" means engine, environment, and promise implementation code. In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack. This can be implemented with either a "macro-task" mechanism such as setTimeout or setImmediate, or with a "micro-task" mechanism such as MutationObserver or process.nextTick. Since the promise implementation is considered platform code, it may itself contain a task-scheduling queue or "trampoline" in which the handlers are called.

This says that .then() handlers must execute asynchronously after the call stack returns to platform code, but leaves it entirely to the implementation how exactly to do that whether it's done with a macro-task like setTimeout() or micro-task like process.nextTick(). So, per this specification, it is not determinate and should not be relied upon.

I find no information about macro-tasks, micro-tasks or the timing of promise .then() handlers in relation to setTimeout() in the ES6 specification. This is perhaps not surprising since setTimeout() itself is not part of the ES6 specification (it is a host environment function, not a language feature).

I haven't found any specifications to back this up, but the answers to this question Difference between microtask and macrotask within an event loop context explain how things tend to work in browsers with macro-tasks and micro-tasks.

FYI, if you want more info on micro-tasks and macro-tasks, here's an interesting reference article on the topic: Tasks, microtasks, queues and schedules.

这篇关于JavaScript Promise 的执行顺序是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆