You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An event loop has one or more task queues. A task queue is a set of tasks.
Each event loop has a microtask queue, which is a queue of microtasks, initially empty. A microtask is a colloquial way of referring to a task that was created via the queue a microtask algorithm.
script start
script end
promise1
promise2
setTimeout
分析:
🌰复杂的事件循环触发
执行:
//<div class="outer">// <div class="inner"></div>//</div>varouter=document.querySelector('.outer');varinner=document.querySelector('.inner');// Let's listen for attribute changes on the outer elementnewMutationObserver(function(){console.log('mutate');}).observe(outer,{attributes: true});// Here's a click listener…functiononClick(){console.log('click');setTimeout(function(){console.log('timeout');},0);Promise.resolve().then(function(){console.log('promise');});outer.setAttribute('data-random',Math.random());}// …which we'll attach to both elementsinner.addEventListener('click',onClick);outer.addEventListener('click',onClick);
什么是事件循环
Event loop是一种程序结构,是实现异步的一种机制。这种异步执行的运行机制如下:
为了更好地理解Event Loop,可以参考下图(转引自Philip Roberts的演讲 What the heck is the event loop anyway )
Node.js的事件循环
当 Node.js 启动后,它会初始化事件轮询;处理已提供的输入脚本,它可能会调用一些异步的 API、调度定时器,或者调用
process.nextTick()
,然后开始处理事件循环下面的图表展示了事件循环操作顺序的简化概览:
每个阶段都有一个FIFO的回调队列(queue)要执行。而每个阶段有自己的特殊之处,简单说,就是当event loop进入某个阶段后,会执行该阶段特定的(任意)操作,然后才会执行这个阶段的队列里的回调。当队列被执行完,或者执行的回调数量达到上限后,event loop会进入下个阶段
阶段概述
setTimeout()
和setInterval()
设定的回调。setImmediate()
的回调。setImmediate()
设定的回调。socket.on('close', ...)
的回调。阶段详情
Timer
一个timer指定 可以执行所提供回调 的阈值,而不是用户希望其执行的确切时间。在指定的一段时间间隔后, timer回调会被尽可能早的运行。但系统调度或者其它回调的执行可能会延迟timer回调的执行。
I/O callbacks
这个阶段执行一些系统操作的回调。比如TCP错误,如一个TCP socket在想要连接时收到
ECONNREFUSED
,类unix系统会等待以报告错误,这就会放到 I/O callbacks 阶段的队列执行。poll
阶段有两个重要的功能:
当事件循环进入 poll 阶段且 没有timer时 ,将发生以下两种情况之一:
一旦 poll 队列为空,event loop将检查
已达到时间阈值的timer
。如果一个或多个timer达到设定时间,则事件循环将绕回计timer阶段以执行这些timer回调。check
这个阶段允许在 poll 阶段结束后立即执行回调。如果 poll 阶段空闲,并且有被
setImmediate()
设定的回调,event loop会转到 check 阶段而不是继续等待。setImmediate()
实际上是一个特殊的timer,跑在event loop中一个独立的阶段。它使用libuv的API来设定在 poll 阶段结束后立即执行回调。通常上来讲,随着代码执行,event loop终将进入 poll 阶段,在这个阶段等待请求连接等时间。但是,只要有被
setImmediate()
设定了回调,一旦 poll 阶段空闲,那么程序将结束 poll 阶段并进入 check 阶段,而不是继续等待poll events。close callbacks
如果一个 socket 或 handle 被突然关掉(比如
socket.destroy()
),close事件将在这个阶段被触发,否则将通过process.nextTick()触发。举例说明Event loop
🌰setimmediate立即执行?
执行:
输出:
分析:
setImmediate
回调,则结束poll 到check阶段🌰setimmediate真的立即执行
执行:
输出:
分析:
setImmediate
的回调永远先执行。所以说setImmediate
真的是立即执行的🌰setImmediate和process. nextTick()
执行:
输出:
分析:
process.nextTick
在当前操作完成后处理,不管目前处于事件循环的哪个阶段,它都会立即执行setImmediate
只能在 check 阶段执行回调其他补充
以上例子中有涉及修改node源码打印执行日志可以参考 node源码编译&使用
浏览器的事件循环
对比Nodejs事件循环
浏览器的事件循环事表现出的状态与node中大致相同。但是浏览器的有自己的一套事件循环模型。
浏览器至少有一个事件循环,一个事件循环至少有一个任务队列。此外每个事件循环都有一个microtask queue。
macrotask(任务队列) & microtask
Macrotasks包含生成dom对象、解析HTML、执行主线程js代码、更改当前URL还有其他的一些事件如页面加载、输入、网络事件和定时器事件。从浏览器的角度来看,macrotask代表一些离散的独立的工作。当执行完一个task后,浏览器可以继续其他的工作如页面重渲染和垃圾回收。
Microtasks则是完成一些更新应用程序状态的较小任务,如处理promise的回调和DOM的修改,这些任务在浏览器重渲染前执行。Microtask应该以异步的方式尽快执行,其开销比执行一个新的macrotask要小。Microtasks使得我们可以在UI重渲染之前执行某些任务,从而避免了不必要的UI渲染,这些渲染可能导致显示的应用程序状态不一致。
上图看出一些细节:
举例说明
🌰一个简易事件循环
执行:
输出:
分析:
🌰复杂的事件循环触发
执行:
输出:
分析:
参考文章
JavaScript 运行机制详解:再谈Event Loop
Node.js的event loop及timer/setImmediate/nextTick
html规范.事件循环
深入理解js事件循环机(浏览器篇)
HTML系列:macrotask和microtask
asks, microtasks, queues and schedules
The text was updated successfully, but these errors were encountered: