大家都知道JS是一门单线程语言,也就意味着JS无法进行多线程,但是JS中异步的概念完全可以模拟多线程,而且效果差不到哪去
要完全理解异步,就需要了解 JS 的运行核心——事件循环(event loop)
但是在浏览器中运行JS和在nodeJS中运行还有一些差别,接下来我们就来看看这些差别在哪里
首先是浏览器中的EventLoop
在说浏览器EventLoop之前我们要先看看浏览器模型
- 用户界面 包括地址栏,书签栏等那些东西
- 浏览器引擎 在用户界面,呈现引擎之间传送指令
- 渲染引擎 也被称为呈现引擎
- 请求(网络) 用于网络调用,比如HTTP请求
- JS解释器 用于解析和执行JS代码
- UI(用户界面后端) 用于绘制样式 和JS共用一个线程
- 数据存储 浏览器需要保存的一些数据 不如Cookie
JS和css公用一个线程是因为浏览器不会同时渲染JS和css,一般都会先渲染css再执行JS
浏览器中的微任务
then
messageChannel
mutationObersve
浏览器中的宏任务
setTimeout
setInterval
代码调用先进堆栈,堆栈是代码的总执行站,堆栈整个执行的过程中会先将微任务,宏任务放到相应的队列中,事件提出来等待触发,等到总执行站中的代码空了,会先看微任务队列中有没有,如果有就会放到总执行站中执行,然后在看宏任务队列中有没有。
浏览器EventLoop.pngsetTimeout(function () {
console.log('setTimeout')
})
Promise.resolve().then(function () {
console.log('promise')
});
console.log('堆栈');
// 执行结果:
// 堆栈
// promise
// setTimeout
如果把微任务队列放到堆栈中执行的时候又发现了宏任务,会顺便吧微任务中的宏任务一起执行了
setTimeout(function () {
console.log('setTimeout1')
Promise.resolve().then(function () {
console.log('promise')
});
})
setTimeout(function () {
console.log('setTimeout2');
});
// 执行结果:
// setTimeout1
// promise
// setTimeout2
然后是nodeJS运行环境中的EventLoop
nodeJS中的宏任务和微任务在浏览器的基础上有新增了几个
微任务
then
nextTick
messageChannel
mutationObersve
宏任务
setTimeout
setInterval
setImmediate、
node的调用顺序中会比浏览器中的复杂一些
nodeJS EventLoop.png看图虽然复杂,但是我们只需要关心timers计时器阶段,poll轮询阶段,check检查阶段(setImmediate回掉),clons关闭阶段以及微任务队列即可,因为处理网络,内部调用与咱们的宏任务和微任务的执行没有太大的关系
和浏览器运行环境不同的是微任务只会在阶段转化的时候才会调用,就是close关闭阶段后再执行下一阶段的时候
process.nextTick(function () {
console.log('nextTick')
});
setImmediate(function () {
console.log('setImmediate')
});
// 执行结果:
// nextTick
// setImmediate
如果宏任务执行的时候又发现微任务了,不会和浏览器一样顺便执行了,而是会将微任务再放到微任务队列中,等待整个阶段结束后,下一个阶段开始的时候先执行完微任务队列中的微任务
setTimeout(function () {
console.log('setTimeout1')
Promise.resolve().then(function () {
console.log('promise')
});
})
setTimeout(function () {
console.log('setTimeout2');
});
// 执行结果:
// setTimeout1
// promise
// setTimeout2
反之亦然
process.nextTick(function () {
console.log(1)
setImmediate(function () {
console.log(2);
})
})
setImmediate(function () {
console.log(3);
process.nextTick(function () {
console.log(4)
})
})
// 执行结果:
// 1
// 3
// 2
// 4
timeout immediate 两个谁先执行不一定 取决于node的执行时间
setTimeout(function () {
console.log('setTimeout');
})
setImmediate(function () {
console.log('setImmediate')
});
// 执行结果:
// setTimeout
// setImmediate
// 或者:
// setImmediate
// setTimeout
但是加上i/o文件操作以后就会先执行setImmediate,因为setImmediate在i/o文件操作后面的那个阶段执行,执行完setImmediate会在下一个阶段的时候再执行setTimeout (timers 计时器执行阶段)
let fs = require('fs');
fs.readFile('./1.txt', function () {
console.log('fs');
setTimeout(function () {
console.log('setTimeout');
})
setImmediate(function () {
console.log('setImmediate')
})
});
// 执行结果
// fs
// setImmediate
// setTimeout
网友评论