1. 单线程
JavaScript
在同一时间内只能做一件事,即JS单线程。也常被称为“阻塞式执行”。
2. 任务队列
‘任务队列’指一个先进先出的队列,它里面存着各种事件和任务。
任务分为两种:同步任务和异步任务
区别同步和异步:有无阻塞。
3. 同步任务
指在任务队列中排队执行的任务,需要等前一个任务执行完才可以执行。
4. 异步任务
异步任务则不会阻塞。
常见异步任务:
setTimeout和setInterval
DOM事件
Promise
process.nextTick
fs.readFile
http.get
异步函数
:如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。
同步任务和异步任务分别进入不同的场所:
- 同步任务进入主线程,异步任务进入
Event Table
并注册回调函数 - 当函数注册完毕后会将这个函数移入
Event Queue
- 主线程内的任务执行完毕后会取
Event Queue
读取对应函数,进入主线程执行
不断重复步骤,形成了事件循环(Event Loop)
如下图:
JS异步机制:遇到宏任务先执行宏任务,将宏任务放入宏任务 的Queue
,然后再执行微任务,将微任务放入微任务的 Queue
。
(即先遇到宏任务先执行宏任务,然后执行所有的微任务,再执行宏任务)
如下图:
5. 宏任务
常见宏任务:
I/O
setTimeout
setInterval
setImmdiate
requestAnimationFrame
6. 微任务
process.nextTick
Promise
Promise.then
MutationObserver
例子1:
setTimeout(() => {
//执行后 回调一个宏事件
console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');
new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3
-
首先浏览器执行js进入第一个宏任务进入主线程, 遇到
setTimeout
分发到宏任务Event Queue
中 -
遇到
console.log()
直接执行 输出外层宏事件1
-
遇到
Promise
,new Promise
直接执行 输出外层宏事件2
-
执行
then
被分发到微任务Event Queue
中 -
第一轮宏任务执行结束,开始执行微任务 打印
微事件1
.微事件2
-
第一轮微任务执行完毕,执行第二轮宏事件,打印
setTimeout
里面内容内层宏事件3
例子2:
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//微事件1
process.nextTick(function() {
console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
//微事件2
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
-
首先浏览器执行js进入第一个宏任务进入主线程, 直接打印
console.log('1')
-
遇到
setTimeout
分发到宏任务Event Queue
中 -
遇到
process.nextTick
丢到微任务Event Queue
中 -
遇到
Promise
,new Promise
直接执行 输出console.log('7')
; -
执行
then
被分发到微任务Event Queue
中 -
第一轮宏任务执行结束,开始执行微任务 打印
6,8
-
第一轮微任务执行完毕,执行第二轮宏事件,执行
setTimeout
-
先执行主线程宏任务,在执行微任务,打印
2,4,3,5
-
在执行第二个
setTimeout
,同理打印9,11,10,12
-
整段代码,共进行了三次事件循环,完整的输出为1,7,6,8,2,4,3,5,9,11,10,12。