We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Node系列-上一节为什么要使用Node
事件循环机制
浏览器的事件循环
Node的事件循环
先了解下任务队列
此时区分为浏览器的事件循环和Node端的事件循环。下面将一一详解。
主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
上图过程是一个宏观的表述,实际上callback queue任务队列是分为macro task宏任务和micro task微任务两种。
callback queue
macro task
micro task
在一个事件循环当中,执行的步骤如下:
从以上我们得知重点是执行栈为空后优先处理微任务再处理宏任务。
写过vue的同学应该熟悉一个方法this.$nextTick(() => { ... }),此时对于宏任务和微任务的分类我们不知所措,那就看看vue源码中是如何处理的。
this.$nextTick(() => { ... })
next-tick.js传送门
其中定义的很清楚
宏任务
微任务
setImmediate/setTimeout
setImmediate为IE特有的,我们可以在IE浏览器环境下做测试
setImmediate(() => { console.log('setImmediate') }) setTimeout(() => { console.log('setTimeout') }, 0) // 延迟设置为0,实际上是会有4ms的延迟。 // => setImmediate // setTimeout
例子代码地址
MessageChannel
H5的API,兼容性不怎么好,前往mdn查看使用
再做如下测试
let channel = new MessageChannel() let port1 = channel.port1 let port2 = channel.port2 port1.postMessage('hello careteen ~') port2.onmessage = function (e) { console.log(e.data) } // => hello careteen ~
promise的then方法
Promise.resolve(100).then(data => { console.log(data) }) // => 100
MutationObserver 这也是一个属于微任务的异步方法,前往mdn查看使用
此方法在vue1.0中有使用到,但是再2.0以后则废弃了。
下面简单介绍下使用,场景为为页面创建多个节点,当节点创建完成后告诉我们。
html
<div id="box"></div>
js
let observer = new MutationObserver(function() { console.log('dom 更新完了') console.log(box.children.length) }) // 监听后代的变化 observer.observe(box, { childList: true }) for (let i = 0; i < 5; i++) { box.appendChild(document.createElement('sapn')) } for (let i = 0; i < 10; i++) { box.appendChild(document.createElement('p')) } // => 当节点创建完成后 // dom 更新完了 // 15
case1:
Promise.resolve().then(() => { console.log('then1') setTimeout(() => { console.log('timer1') }, 0) }) console.log('start') setTimeout(() => { console.log('timer2') Promise.resolve().then(() => { console.log('then2') }) }, 0) // => start -> then1 -> timer2 -> then2 -> timer1
根据上面的执行流程可得知结果。
start
then1
timer2
then2
以上也只是宏观上的一个描述,和浏览器一样,异步事件方法的队列也细分了几个。前往Node官网可查看详细说明
下面摘自Node官网
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
从上面的模型中我们可以得知机制的执行顺序:
外部输入数据-->轮询阶段(poll)-->检查阶段(check)-->关闭事件回调阶段(close callback)-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)-->轮询阶段...
细分的几个异步队列:
setTimeout
setInterval
fs.readFile()
setImmediate()
socket.on('close', ...)
和浏览器的事件循环机制有所不同:
首先会在poll阶段
poll
poll queue
setImmediate
check
timer
timer queue
I/O callback
check阶段
close阶段
close
socket.destroy()
process.nextTick()
timer阶段
I/O callback阶段
如此反复循环
特别的:process.nextTick()这个方法虽然没有在上面列入,但是却在每个阶段执行完毕准备进入下一个阶段时优先调用
上面说了一大推,看起来很枯燥,那么下面来几个case深入理解
Promise.resolve().then(() => { console.log('then1') setTimeout(() => { console.log('timer1') }, 0) }) setTimeout(() => { console.log('timer2') Promise.resolve().then(() => { console.log('then2') }) }, 0) // => then1 -> timer2 -> then2 -> timer1 // or then1 -> timer2 -> timer1 -> then2
上面代码的结果有两种可能,then2执行后,timer1可能还没到时间也可能到时间了,因为setTimeout方法第二个参数如果设置为0,实际执行时会有4ms的延迟。
timer1
0
4ms
case2:
setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('setImmediate') }) // => setImmediate -> timeout // or timeout -> setImmediate
多运行几次,运行结果是不一定的,这取决于运行代码的环境。运行和环境中的各种复杂情况会导致在同步队列里两个方法的顺序随机决定。
再来看一个例子case3:
let fs = require('fs') fs.readFile('1.setImmediate.html', () => { setTimeout(() => { console.log('timeout') }, 0) setImmediate(() => { console.log('setImmediate') }) }) // => setImmediate -> timeout
回顾上面提到的阶段,在I/O事件的回调中,setImmediate的回调永远优先于setTimeout的回调执行。所以返回结果的顺序是固定的。
I/O事件
Node系列-下一节手摸手带你撸一个commonjs规范
The text was updated successfully, but these errors were encountered:
No branches or pull requests
事件循环详解
Node系列-上一节为什么要使用Node
目录
事件循环机制
浏览器的事件循环
Node的事件循环
事件循环机制
先了解下任务队列
此时区分为浏览器的事件循环和Node端的事件循环。下面将一一详解。
浏览器的事件循环
主线程从任务队列中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
上图过程是一个宏观的表述,实际上
callback queue
任务队列是分为macro task
宏任务和micro task
微任务两种。在一个事件循环当中,执行的步骤如下:
从以上我们得知重点是执行栈为空后优先处理微任务再处理宏任务。
浏览器中的宏任务和微任务有哪些
写过vue的同学应该熟悉一个方法
this.$nextTick(() => { ... })
,此时对于宏任务和微任务的分类我们不知所措,那就看看vue源码中是如何处理的。next-tick.js传送门
其中定义的很清楚
宏任务
微任务
setImmediate/setTimeout
setImmediate为IE特有的,我们可以在IE浏览器环境下做测试
例子代码地址
MessageChannel
H5的API,兼容性不怎么好,前往mdn查看使用
再做如下测试
例子代码地址
promise的then方法
MutationObserver
这也是一个属于微任务的异步方法,前往mdn查看使用
此方法在vue1.0中有使用到,但是再2.0以后则废弃了。
下面简单介绍下使用,场景为为页面创建多个节点,当节点创建完成后告诉我们。
html
js
例子代码地址
面试题详解
case1:
例子代码地址
根据上面的执行流程可得知结果。
start
,then1
timer2
then2
Node的事件循环
以上也只是宏观上的一个描述,和浏览器一样,异步事件方法的队列也细分了几个。前往Node官网可查看详细说明
下面摘自Node官网
从上面的模型中我们可以得知机制的执行顺序:
外部输入数据-->轮询阶段(poll)-->检查阶段(check)-->关闭事件回调阶段(close callback)-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)-->轮询阶段...
细分的几个异步队列:
setTimeout
方法setInterval
方法fs.readFile()
方法setImmediate()
的回调会在这个阶段执行socket.on('close', ...)
这种close事件的回调和浏览器的事件循环机制有所不同:
首先会在
poll
阶段poll queue
中是否有事件setImmediate
的回调check
阶段去执行这些回调timer
timer
的回调按照调用顺序放到timer queue
,然后循环进入timer
阶段执行队列中的回调setImmediate
和timer
的判断顺序不是固定的,受代码运行环境的影响setImmediate
和timer
的队列都是空的,那么循环会在poll
阶段停留,直到有一个I/O事件返回,循环会进入I/O callback
阶段并立即执行这个事件的回调check
阶段setImmediate
的回调,当poll
阶段进入空闲状态,并且setImmediate
队列中有callback时,事件循环进入这个阶段close
阶段socket.destroy()
方法),close事件会被发送到这个阶段执行回调。否则事件会用process.nextTick()
方法发送出去timer
阶段I/O callback
阶段如此反复循环
特别的:
process.nextTick()
这个方法虽然没有在上面列入,但是却在每个阶段执行完毕准备进入下一个阶段时优先调用poll queue
的任务不同的是,这个操作在队列清空前是不会停止的。也就是说错误使用会导致node进入一个死循环,直到内存泄露面试题详解
上面说了一大推,看起来很枯燥,那么下面来几个case深入理解
case1:
例子代码地址
上面代码的结果有两种可能,
then2
执行后,timer1
可能还没到时间也可能到时间了,因为setTimeout
方法第二个参数如果设置为0
,实际执行时会有4ms
的延迟。case2:
例子代码地址
多运行几次,运行结果是不一定的,这取决于运行代码的环境。运行和环境中的各种复杂情况会导致在同步队列里两个方法的顺序随机决定。
再来看一个例子case3:
例子代码地址
回顾上面提到的阶段,在
I/O事件
的回调中,setImmediate
的回调永远优先于setTimeout
的回调执行。所以返回结果的顺序是固定的。友情链接
Node系列-下一节手摸手带你撸一个commonjs规范
The text was updated successfully, but these errors were encountered: