JS是单线程语言,始终只有一个线程执行JS代码(Web Worker没有改变JS单线程的本质),对于异步操作,是通过事件循环(Event Loop)来实现的。
任务
每个异步操作都是一个任务,任务分为宏任务和微任务
- 宏任务:script、setTimeout、setInterval、setImmediate、I/O、UI交互事件
- 微任务:Promise(Promise的then和catch)、process.nextTick、Object.observe(已废弃)
其中, setImmediate
和process.nextTick
是Node独有的,UI交互事件
肯定是浏览器独有的。
澄清两个误区
- 事件循环和JS代码是在一个线程中运行的
- 经过测试,不管是浏览器端还是Node端,每个宏任务执行完后,都会去清除微任务
特别是第二点,可能Node做了更新(当前时间是2019-01-13,Node v11.4.0),如下面代码,不管浏览器还是Node,都会输出:1 2 3 4
setTimeout(function () { new Promise(function (resolve) { console.log(1); resolve(); }).then(function () { console.log(2); });});setTimeout(function () { new Promise(function (resolve) { console.log(3); resolve(); }).then(function () { console.log(4); });});// delay(); // 为了消除定时器最小延迟带来的影响,可以执行一下耗时操作,确保上面两个setTimeout都触发了function delay() { for (let i = 0; i < 1000000000; i++) {}}复制代码
定时器的最小延迟问题见
差异
浏览器的事件循环是HTML5定义的规范,Node的事件循环是libuv库实现的,但截至目前(2019-01-13),两者的表现基本一致,Node的事件循环比浏览器要复杂,下面看Node的事件循环。
Node的事件循环
Node的事件循环,每一轮有六个阶段
1. Timer阶段
执行可用的setTimeout/setInterval回调
2. I/O callbacks阶段
执行可用的I/O回调,除了setTimeout
、setInterval
、setImmediate
、Close callbacks
都属于这个阶段。
3. idle, prepare阶段
这两个阶段主要是Node做一些内部操作,忽略。
4. Poll阶段
这是轮询阶段,如果没有可用的setTimeout/setInterval/setImmediate/Close callbacks回调,会一直停留在这个阶段,等待I/O回调。
这个阶段不会一直停留,达到一定条件后,会到下一个阶段。
5. Check阶段
专门用于执行setImmediate
。
6. Close callbacks阶段
关闭请求在这里执行(socket.on('close', ()=>{})),这个阶段就像一个清理阶段。
如果事件循环还活着,就继续下一轮循环。
最后
欢迎关注我的微博