为什么有事件循环
-JS单线程
JavaScript设计之初就是单线程的,主要用途是与用户互动,以及操作DOM。为了避免两个线程同时操作DOM的情况发生,就采用了单线程。即使H5提出了web worker标准,它有很多限制,受主线程控制,是主线程的子线程。
-非阻塞
通过EventLoop实现
宏任务和微任务
宏任务和微任务
为什么要引入微任务,只有一种类型的任务不行么?
页面渲染事件,各种IO的完成事件等随时被添加到任务队列中,一直会保持先进先出的原则执行,我们不能准确地控制这些事件被添加到任务队列中的位置。但是这个时候突然有高优先级的任务需要尽快执行,那么一种类型的任务就不合适了,所以引入了微任务队列。
浏览器里的事件循环
关于微任务和宏任务在浏览器的执行顺序是这样的:
执行一只task(宏任务)
执行完micro-task队列 (微任务)
如此循环往复下去
常见的 task(宏任务) 比如:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
常见的 micro-task 比如: new Promise().then(回调)、MutationObserver(html5新特性) 等。
Node里的事件循环
大体的task(宏任务)执行顺序是这样的:
timers定时器:本阶段执行已经安排的 setTimeout() 和 setInterval() 的回调函数。
pending callbacks待定回调:执行延迟到下一个循环迭代的 I/O 回调。
idle, prepare:仅系统内部使用。
poll 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,它们由计时器和 setImmediate() 排定的之外),其余情况 node 将在此处阻塞。
check 检测:setImmediate() 回调函数在这里执行。
close callbacks 关闭的回调函数:一些准备关闭的回调函数,如:socket.on('close', ...)。
微任务和宏任务在Node的执行顺序
Node 10以前:
执行完一个阶段的所有任务
执行完nextTick队列里面的内容
然后执行完微任务队列的内容
Node 11以后:
和浏览器的行为统一了,都是每执行一个宏任务就执行完微任务队列。