我无法理解以下代码是如何运行的。为什么“1”在“b”之后但“h”在“3”之后?订单应该是:a,b,1,2,h,3?有些文章说“事件循环队列”和“作业队列”之间的区别导致了以下输出。但是怎么样?我已经阅读了规范 ECMAScript 2015 - 8.4工作和作业队列,想知道Promise'job是如何工作的,但这让我更加困惑。有人能帮我吗?谢谢!
var promise = new Promise(function(resolve, reject) {resolve(1)});
promise.then(function(resolve) {console.log(1)});
console.log('a');
promise.then(function(resolve) {console.log(2);});
setTimeout(function() {console.log('h')}, 0);
promise.then(function(resolve) {console.log(3)});
console.log('b');
// a
// b
// 1
// 2
// 3
// h
我知道Promise是异步的,但是setTimeout(..)异步操作的回调总是在Promise的异步操作之后。为什么?
为什么“1”在“b”之后?
承诺规范,所有承诺 .then()
在JS的当前线程运行完成之后,异步调用处理程序。因此,两者 a
和 b
作为当前JS的一部分同步执行的将在任何之前执行 .then()
处理程序如此 1
永远都会追随 a
和 b
。
一些有趣的读物:T问,微任务,队列和时间表 和 在javascript承诺中执行的顺序是什么 和 编写JavaScript框架 - 执行时间,超出setTimeout。
这个帖子里有一些很好的建议:承诺在两者之间摇摆不定 nextTick
和 setImmediate
:
我不建议依赖于确切的执行顺序
非链式事件。如果要控制执行顺序 -
以某种方式重新排列回调,以便成为您想要的回调
稍后执行取决于您要执行的那个
更早,或实现一个队列(在幕后做同样的事情)。
换句话说,如果您依赖于异步事件的特定时间,那么您实际上应该在它们的代码中将它们链接起来,这样就必须通过代码在另一个之后发生,而不是依赖于实现中的未指定的调度。
在HTML术语中, 事件循环 对于来自同一域的页面或一组页面,可以有多个页面 任务队列。任务来自同一个 任务来源 总是进入同一个队列,浏览器选择下一个要使用的任务队列。
运行计时器回调的任务来自 计时器任务源 并进入同一队列。我们叫这个队列 任务队列“A”。
ECMAscript 2015(ES6)规范要求任务运行Promise反应回调以形成自己的作业队列 “PromiseJobs”。 ECMAscript和HTML规范不使用相同的语言,所以让我们在概念上将ECMA的“Promise Job queue”与HTML等同起来 任务队列“B” 在浏览器中 - 至少与计时器使用的队列不同。
从理论上讲,浏览器可以从队列A或B中选择任务来运行,但实际上也是如此 promise任务队列获得更高的优先级,并在计时器回调运行之前清空。
这就是最后记录“h”的原因。诺言 then
调用履行的promises将作业放在promise队列中,该队列的执行优先级高于计时器回调。承诺队列之后才变为空 console.log(3)
已执行,允许定时器回调执行。
高级
ECMAScript监护人选择不在其规范中使用HTML5术语或任务队列描述,因为ECMAScript可以在比HTML浏览器更多的环境中运行。
承诺队列的本机实现可以使用“微任务”队列而不是单独的专用承诺任务队列。在当前脚本线程和先前添加到微队列的任何任务完成之后,只需运行微排队作业。
理解承诺不需要微任务排队的细节。
对于缺乏对promises的本机支持(所有IE版本等)的浏览器,承诺polyfill可能会使用计时器,并且当它涉及promise响应和计时器回调的顺序时,其行为与本机实现完全不同。
我发现这个对JS新手很容易理解。
这是来自@ getify的书的复制粘贴
使用一个比喻:事件循环队列就像一个游乐园骑行,一旦你完成骑行,你必须再去线路后面再骑。但是工作队列就像完成骑行一样,但随后排成一排并重新开始。
事件循环队列 - 对于除promise之外的所有异步回调,h
作业队列 - 用于与promises相关的所有异步回调。 1,2,3
同步 - a,b