为了彻底弄懂JS执行顺序,网上找来了一些经典案例和图解,但不知道资源的最终来源,所以未注明出处,如有涉及版权问题,请及时联系
先来做一套测试题
//1.
for (var i = 0; i < 5; i++) {
console.log(i);
}
//0 1 2 3 4
//2.
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000 * i);
}
//5 5 5 5 5
//3.
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
//0 1 2 3 4
//4.
for (var i = 0; i < 5; i++) {
(function() {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
//5 5 5 5 5
//5.
for (var i = 0; i < 5; i++) {
setTimeout((function(i) {
console.log(i);
})(i), i * 1000);
}
//0 1 2 3 4
//6.
setTimeout(function() {
console.log(1)
}, 0);
new Promise(function executor(resolve) {
console.log(2);
for( var i=0 ; i<10000 ; i++ ) {
i == 9999 && resolve();
}
console.log(3);
}).then(function() {
console.log(4);
});
console.log(5);
//2 3 5 4 1
//7.
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
/*
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/
JS事件循环(Event Loop)
- JS是单线程的,所有任务都在主线程中执行
- 任务分为同步任务和异步任务
- 同步任务先进入主线程排队执行,排队的地方叫:执行栈
- 异步任务先挂起,有返回结果或回调时,加入:任务队列
- 主线程任务全部执行完毕后,领取任务队列里的任务进入主线程继续执行
- 异步任务分为两种:宏任务(macrotask) 和 微任务(microtask),执行任务的顺序:先 微任务 后 宏任务
- 宏任务:
- setTimeOut
- setInterval
- setImmediate(Node)
- requestAnimationFrame(浏览器)
- run
- I/O
- UI rendering 微任务:
- process.nextTick(Node)
- MutationObserver(浏览器)
- Promise的 .then .catch .finally
案例分析
// node环境
console.log('1');
setTimeout(function() {//setTimeout1
console.log('2');
process.nextTick(function() {//process2
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {//then2
console.log('5')
})
})
process.nextTick(function() {//process1
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {{//then1
console.log('8');
})
setTimeout(function() {//setTimeout2
console.log('9');
process.nextTick(function() {//process3
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {//then3
console.log('12');
})
})
第一轮循环:
- 首先打印 1
- 接下来是setTimeout是异步任务且是宏任务,加入宏任务暂且记为 setTimeout1
- 接下来是 process 微任务 加入微任务队列 记为 process1
- 接下来是 new Promise 里面直接 resolve(7) 所以打印 7 后面的then是微任务 记为 then1
- 最后是 setTimeout 宏任务 记为 setTimeout2 第一轮循环打印出的是 1 7
- 当前宏任务队列:setTimeout1, setTimeout2
- 当前微任务队列:process1, then1,
第二轮循环:
- 执行所有微任务
- 执行process1,打印出 6
- 执行then1 打印出8
- 微任务都执行结束了,开始执行第一个宏任务
- 执行 setTimeout1 也就是 第 3 - 14 行
- 首先打印出 2
- 遇到 process 微任务 记为 process2
- new Promise中resolve 打印出 4
- then 微任务 记为 then2
第二轮循环结束,当前打印出来的是 1 7 6 8 2 4
- 当前宏任务队列:setTimeout2
- 当前微任务队列:process2, then2
第三轮循环:
- 执行所有的微任务
- 执行 process2 打印出 3
- 执行 then2 打印出 5
- 执行第一个宏任务,也就是执行 setTimeout2 对应代码中的 25 - 36 行
- 首先打印出 9
- process 微任务 记为 process3
- new Promise执行resolve 打印出 11
- then 微任务 记为 then3
第三轮循环结束,当前打印顺序为:1 7 6 8 2 4 3 5 9 11
- 当前宏任务队列为空
- 当前微任务队列:process3,then3
第四轮循环:
- 执行所有的微任务
- 执行 process3 打印出 10
- 执行 then3 打印出 12
第四轮循环结束,最终打印顺序为:1 7 6 8 2 4 3 5 9 11 10 12