首先,js 是单线程的语言,eventloop是js的执行机制,在不同的运行环境(浏览器或Nodejs)下,执行顺序会略有不同。
js执行过程中,任务分为同步任务和异步任务,异步任务又分为宏任务(macrotask)和微任务(microtask)。
其中宏任务:script(整体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、Messagechannel、setImmediate(Nodejs环境)。
微任务:Promise.then、MutationObserver(监听DOM变化)、process.nextTick(Nodejs环境)。
宏任务和微任务是两个不同的对列。
具体的执行步骤如下:
1.从宏任务的头部取出一个任务进行执行;
2.执行过程中,如果是同步任务,直接执行,如果是宏任务,加入宏任务对列,如果是微任务,加入微任务对列。
3.当前宏任务执行完毕,查看当前的微任务对列,如果有,则执行微任务。
4.同样,微任务执行过程中,如果是同步任务,直接执行,如果是宏任务,继续加入宏任务对列,如果是微任务,继续加入微任务对列。
5.微任务执行完毕,则继续从宏任务对列中取出下一个宏任务。
6.如此循环,直到所有任务全部执行完毕,这就是eventloop。
来个例子:
console.log(1)
new Promise((resolve, reject) => {
resolve(2);
console.log(3);
}).then(r => {
console.log(r);
});
console.log(4)
//打印结果是 1 3 4 2
//首先当前的这段script代码属于宏任务,开始执行,首先执行同步任务,先打印1,
//然后遇到一个立即resolve的resolve(2),会触发promise.then,但是由于promise.then是微任务,把它放到微任务对列。
//接着执行,然后同步任务打印3,
//接着打印4
//当前一轮的宏任务执行完成,去执行当前微任务对列的console.log(2);
再改动一点:
console.log(1)
new Promise((resolve, reject) => {
setTimeout(function(){
console.log(5);
},0);
resolve(2);
console.log(3);
}).then(r => {
console.log(r);
});
console.log(4)
//打印结果是 1 3 4 2 5
//首先当前的这段script代码属于宏任务,开始执行,首先执行同步任务,先打印1,
//然后遇到一个setTimeout,虽然他的倒计时是0秒,但是它是宏任务,需要放到宏任务对列。
//接下来一个立即resolve的resolve(2),会触发promise.then,但是由于promise.then是微任务,把它放到微任务对列。
//接着执行,然后同步任务打印3,
//接着打印4
//当前一轮的宏任务执行完成,去执行当前微任务对列的console.log(2);
//接着去执行宏任务队列里的另一个宏任务console.log(5);
再改动一下:
console.log(1)
new Promise((resolve, reject) => {
setTimeout(function(){
console.log(5);
new Promise((resolve,reject)=>{
console.log(7);
resolve(6)
}).then(r =>{
console.log(r)
})
},0);
resolve(2);
console.log(3);
}).then(r => {
console.log(r);
});
console.log(4)
//打印结果 1 3 4 2 5 7 6
//执行步骤同上方
//首先当前的这段script代码属于宏任务,开始执行,首先执行同步任务,先打印1,
//然后遇到一个setTimeout,虽然他的倒计时是0秒,但是它是宏任务,需要放到宏任务对列。
//接下来一个立即resolve的resolve(2),会触发promise.then,但是由于promise.then是微任务,把它放到微任务对列。
//接着执行,然后同步任务打印3,
//接着打印4
//当前一轮的宏任务执行完成,去执行当前微任务对列的console.log(2);
//接着去执行宏任务队列里的另一个宏任务
//首先执行同步任务console.log(5);
//然后接下来一个立即resolve的resolve(6),xxxxxx(循环上方步骤)
再改动:
console.log(1)
new Promise((resolve, reject) => {
setTimeout(function(){
console.log(5);
resolve(2);
new Promise((resolve,reject)=>{
console.log(7);
resolve(6)
}).then(r =>{
console.log(r)
})
},0);
console.log(3);
}).then(r => {
console.log(r);
});
console.log(4)
// 1 3 4 5 7 2 6
关于setTimeout的倒计时是从什么时候开始的?
我个人赞成一种说法:是在执行到当前位置的时候,就已经开始计时了,然后把回调里的代码插入到设置的倒计时的宏任务的位置中等待执行。
具体看下面几个例子。
setTimeout(function(){
console.log(1)
},1000)
new Promise(function(resolve,reject){
console.log(2);
resolve();
setTimeout(function(){
console.log(3);
},2000)
}).then(function(){
console.log(4);
})
console.log(5);
// 打印结果2 5 4 1 3
//-------------------------------------------------------------------------
setTimeout(function(){
console.log(1)
},1000)
new Promise(function(resolve,reject){
console.log(2);
resolve();
setTimeout(function(){
console.log(3);
},200)
}).then(function(){
console.log(4);
})
console.log(5);
// 打印结果2 5 4 3 1
//虽然先把log(1)的宏任务放入宏任务对列,但是log(3)的宏任务倒计时时间比较短。
//也就是说log(3)是在宏任务的200ms处执行,log(1)是宏任务的1000ms处执行。
参考:
https://www.xiabingbao.com/post/javascript/js-eventloop.html
https://segmentfault.com/q/1010000020315084/
网友评论