当前位置: 首页 > Web前端 > HTML5

Tasks,microtasks,queues,andscheduling

时间:2023-04-05 19:48:11 HTML5

原文地址(英文):https://jakearchibald.com/201...跟MattGaunt(作者的同事)说的时候,我正打算写一篇关于阅读时的文章关于事件循环(eventloop)系统中微任务的队列和执行的文章,他说:“说实话,Jake,我对这篇文章不感兴趣。”好吧,无论如何,既然我已经写完了,让我们坐下来好好享受吧,好吗?事实上,如果视频更适合你,PhilipsRoberts在JSConf上关于事件循环的演讲是一个很好的参考(演讲不涉及微任务,但它很好地涵盖了事件循环的其余部分。好吧),没有进一步废话不多说,进入正题。这是一段JavaScript:console.log('scriptstart');setTimeout(function(){console.log('setTimeout');},0);Promise.resolve().then(function(){console.log('promise1');}).then(function(){console.log('promise2');});console.log('脚本结束');想一想控制台打印结果的顺序吗?正确答案是:scriptstart,scriptend,promise1,promise2,setTimeout,但不同浏览器的结果可能不同。MicrosoftEdge、Firefox40、iOSSafari和桌面版Safari8.0.8会在promise1、promise2之前打印setTimeout,虽然这可能是浏览器厂商竞争的结果,但有点奇怪,因为Firefox39和Safari8.0.7的结果得到的总是正确的。为什么会这样要理解原因,您需要了解事件循环如何处理任务和微任务。当这些术语第一次出现时,你可能会头疼,没关系,深吸一口气……每个“线程”都有自己的事件循环(eventloop),也就是说每个webworker都会有自己的事件循环和独立运行,互不干扰。但是,同源的所有窗口共享一个事件循环(eventloop),这样它们就可以同步通信(译者注:根据HTML5.是webworker)。事件循环一直在运行,执行队列中的任务。一个事件循环有多个任务源,保证了任务在特定任务源Queue上的执行顺序),但是在每个循环中,浏览器会独立选择先执行哪个源任务,保证了一些性能的优先级——敏感任务,例如用户输入。任务(译者注:也叫宏任务)放在任务源中,浏览器内部执行转移到JavaScript/DOM域,保证这些任务按顺序执行。在任务执行期间,浏览器可能会更新渲染。来自鼠标点击的事件回调需要安排一个任务,解析HTML和setTimeout也是如此。setTimeout在等待给定的延迟时间后为其回调创建一个新任务。这就是为什么setTimeout在scriptend之后打印scriptstart的原因,因为scriptend属于第一个task,而setTimeout对应的是另一个task。到目前为止,我们即将弄清楚。需要你有足够的耐心阅读部分Microtasks队列通常用于存储脚本执行后应该立即执行的任务,比如对一批动作做出反应,或者异步操作,避免造成的性能浪费通过创建一个全新的任务。每次在事件循环中,如果没有其他的JavaScript在运行并且执行了任务(task),那么回调之后就会执行microtask。在微任务中排队的任何其他微任务将被添加到队列的末尾并被处理。Microtasks包括MutationObserver、Promise回调(译者注:Microtasks包括:process.nextTick(Nodejs)、Promises、Object.observe、MutationObserver;tasks(任务)包括:script(整体代码)、setTimeout、setInterval、setImmediate、I/O、界面渲染)。处于已结算状态的承诺(直接调用resolve或reject)或已结算的承诺(异步请求已结算)将立即将其回调(然后)放入微任务队列中。这确保了promise的回调是异步的,即使promise已经确定。因此,调用.then(yey,nay)的settledpromise会立即将microtask任务添加到microtasks任务队列中。这就是为什么在脚本结束后打印promise1和promise2的原因,因为运行的代码必须在可以处理微任务之前完成。promise1和promise2在setTimeout之前打印,因为微任务总是在下一个任务之前执行。我们一步一步来分析,(译者注:跳转到原文一步一步的例子,对理解本文很有用)为什么在某些浏览器上结果不一样?一些浏览器打印的结果是:scriptstart,scriptend,setTimeout,promise1,promise2。这些浏览器在promise回调之前调用了setTimeout。浏览器很可能将promise回调视为新任务(task)的一部分,而不是微任务(microtask)。这个错误在某种程度上是可以原谅的,因为承诺规范是从ECMAScript派生的,而不是HTML。ECMAScript定义了类似于microtasks的“作业”概念,但除了一些模糊的电子邮件讨论外,(作业和微任务)之间的关系并不明确。但普遍的共识是承诺应该是微任务的一部分。将promises当作任务会导致一些性能问题,对于一些相关的任务(比如渲染)回调不一定会延迟。由于与其他任务源的交互并中断与其他API的交互,这也会导致一些不确定性。迫切需要将承诺归类为微任务。Webkit(Safari核心)一直在做正确的事情,我认为Safari最终会解决这个问题,事实上,Firefox43已经解决了这个问题。真正有趣的是,Safari和Firefox都在这里进行了一次回归,此后问题已得到修复。我想知道这是不是巧合。译者注task->microtask->uirender对于promise,then回调会在resolve(resolveorreject)后被推入microtaks队列参考Promise中JavaScript中的EventLoop、Tasks和Microtasks