摘要:由此可以认为,实际上是一系列的回调函数集合。此时浏览器会在其它线程中执行异步操作,操作完成后将回调函数放入主线程任务队列中。当主线程将执行栈中的函数执行完毕后,再次读取任务队列,形成循环。
先看一段代码,也是一道经典面试题:
(function test() { setTimeout(function() {console.log(4)}, 0); new Promise(function executor(resolve) { console.log(1); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(2); }).then(function() { console.log(5); }); console.log(3); })()
其输出结果为:
// 1 // 2 // 3 // 5 // 4
我们知道,JavaScript 在同一时间片内只能执行一个任务:
主线程会依次执行代码,当执行到函数的时候会将函数加入执行栈,当函数执行完毕后再将其出栈,直至代码执行完毕。当执行栈为空时,runtime 会从任务队列(先入先出)中取出待执行的回调函数并执行,入栈、出栈的过程同上。这个机制就叫做 Event Loop。
由此可以认为,Event Loop实际上是一系列的回调函数集合。
举例来说:在浏览器中,对于网络请求等需要等待一段时间才会返回结果的操作,我们通常采用异步回调来处理,这个回调就会放入任务队列中。此时浏览器会在其它线程中执行异步操作,操作完成后将回调函数放入主线程任务队列中。Event Loop负责在主线程执行完毕后将任务队列中的函数放入执行栈中,由主线程执行。当主线程将执行栈中的函数执行完毕后,再次读取任务队列,形成循环。所以即使主线程阻塞了,任务队列依然能够被添加函数,因为任务队列的添加是由浏览器负责的。(不同的 runtime 实现可能不同)
另外需要注意的是 Promise.then 是异步执行的,而创建 Promise 实例是同步执行的。这就解释了为什么1、2、3输出在4、5之前。
但为什么5 会输出在4前面呢?
JavaScript 中的任务又分为MacroTask 与 MicroTask 两种。
典型的 MacroTask 包含了 :
setTimeout
setInterval
setImmediate
requestAnimationFrame
I/O
UI rendering
而常见的MicroTask 包含了
process.nextTick
Promises
Object.observe
MutationObserver
Event Loop 中有一个或多个Task Queue,即MacroTask Queue,仅有一个Job Queue,即MicroTask Queue。Task Queue的执行是按照回调顺序先入先出,而在 MacroTask 的执行间隙中会清空已有的 MicroTask Queue
回到代码中,setTimeout(function() {console.log(4)}, 0); 既然延迟设置为0,为什么5会在4之前输入呢?
那是因为setTimeout设置为0的时候,runtime其实并不是0,在主流浏览器中会将其设置为4,而 node 则会将其设置为1。那么现在代码的执行顺序就很清晰了:
console.log(1); // 创建 Promise 主线程执行 ... console.log(2); // 创建 Promise 主线程执行 console.log(3); // test 函数立即执行, 主线程执行 ... console.log(5); // 主线程执行完毕,执行MicroTask Queue,即 promise.then ... console.log(4); // 执行 setTimeout(4)
参考资料:
https://developer.mozilla.org...
https://html.spec.whatwg.org/...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/94529.html
摘要:如果当前没有事件也没有定时器事件,则返回。相关资料关于的架构及设计思路的事件讨论了使用线程池异步运行代码。下一篇初窥事件机制的实现二中定时器的实现 在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力;在Node.js中,事件驱动模型则是其高并发能力的基础。 学习JavaScript也需要了解它的运行平台,为了更好的理解JavaScript的事...
摘要:但是导致了很明显的性能问题。上述两个例子其实是在这个中找到的,第一个使用的版本是,这个版本的实现是采用了,而后因为的里的有,于是尤雨溪更改了实现,换成了,也就是后一个所使用的。后来尤雨溪了解到是将回调放入的队列。 结论 对于event loop 可以抽象成一段简单的代码表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:但是导致了很明显的性能问题。上述两个例子其实是在这个中找到的,第一个使用的版本是,这个版本的实现是采用了,而后因为的里的有,于是尤雨溪更改了实现,换成了,也就是后一个所使用的。后来尤雨溪了解到是将回调放入的队列。 结论 对于event loop 可以抽象成一段简单的代码表示 for (macroTask of macroTaskQueue) { // 1. Handle cur...
摘要:心塞塞根据规范,事件循环是通过任务队列的机制来进行协调的。等便是任务源,而进入任务队列的是他们指定的具体执行任务回调函数。然后当前本轮的结束,主线程可以继续取下一个执行。 依然是:经济基础决定上层建筑。 说明 首先,旨在搞清常用的同步异步执行机制 其次,暂时不讨论node.js的Event Loop执行机制,以下关于浏览器的Event Loop执行机制 最后,借鉴了很多前辈的研究文...
摘要:前言前几天在理解的事件环机制中引发了我对浏览器里的好奇。接下来理解浏览器中的,先看一张图堆和栈堆是用户主动请求而划分出来的内存区域,比如你,就是将一个对象存入堆中,可以理解为存对象。废话不多说,直接上图个人理解。参考资料运行机制详解再谈 前言 前几天在理解node的事件环机制中引发了我对浏览器里Event Loop的好奇。我们都知道javascript是单线程的,任务是需要一个一个按顺...
摘要:曾经的理解首先,是单线程语言,也就意味着同一个时间只能做一件事,那么为什么不是多线程呢这样还能提高效率啊假定同时有两个线程,一个线程在某个节点上编辑了内容,而另一个线程删除了这个节点,这时浏览器就很懵逼了,到底以执行哪个操作呢所以,设计者把 Event Loop曾经的理解 首先,JS是单线程语言,也就意味着同一个时间只能做一件事,那么 为什么JavaScript不是多线程呢?这样还能提...
阅读 3054·2023-04-26 00:40
阅读 2391·2021-09-27 13:47
阅读 4197·2021-09-07 10:22
阅读 2966·2021-09-06 15:02
阅读 3307·2021-09-04 16:45
阅读 2484·2021-08-11 10:23
阅读 3599·2021-07-26 23:38
阅读 2900·2019-08-30 15:54