宏任务、微任务有哪些
- 宏任务:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
- 微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
宏任务、微任务是怎么执行的
先执行同步代码,遇到宏任务则将宏任务放入宏任务队列中,遇到微任务则将微任务放入微任务队列中,当所有同步代码执行完毕后,再将微任务从队列中调入主线程执行,微任务执行完毕后再将宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕
注意: 宏任务进入任务队列后就已经在开始执行, 并不是当执行到宏任务时才开始执行, 看下面代码
function sleep(time){
var timeStamp = new Date().getTime();
var endTime = timeStamp + time;
while(true){
if (new Date().getTime() > endTime){
return;
}
}
}
console.log(1)
setTimeout(()=>{console.log(2);},2000)
sleep(2000)
setTimeout(()=>{console.log(3);},1000)
sleep(2000)
console.log(4)
含promise的代码段
new Promise(() => {}).then() ,我们来看这样一个Promise代码
前面的 new Promise() 这一部分是一个构造函数,这是一个同步任务
后面的 .then() 才是一个异步微任务,这一点是非常重要的
含async await的代码段
async/await本质上还是基于Promise的一些封装,而Promise是属于微任务的一种 所以在使用await关键字与Promise.then效果类似
await 后面可以跟任何的JS 表达式。虽然说 await 可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。
要注意的是如果 await 的是 promise对象,await 会暂停 async 函数内后面的代码,先执行 async 函数外的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果返回后,再继续执行 async 函数内后面的代码
例子1
setTimeout(function(){
console.log(1);
});
new Promise(function(resolve){
console.log(2);
resolve();
}).then(function(){
console.log(3);
}).then(function(){
console.log(4)
});
console.log(5);
// 2 5 3 4 1
- 遇到setTimout,异步宏任务,放入宏任务队列中
- 遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
- Promise.then,异步微任务,将其放入微任务队列中
- 遇到同步任务console.log(5);输出5;主线程中同步任务执行完
- 从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
- 从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空
例子2
setTimeout(()=>{
new Promise(resolve =>{
resolve();
}).then(()=>{
console.log('test');
});
console.log(4);
});
new Promise(resolve => {
resolve();
console.log(1)
}).then( () => {
console.log(3);
Promise.resolve().then(() => {
console.log('before timeout');
}).then(() => {
Promise.resolve().then(() => {
console.log('also before timeout')
})
})
})
console.log(2);
//1,2,3,before timeout,also before timeout,4,test;
- 遇到setTimeout,宏任务,放入宏任务队列中;
- 遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1;
- 而Promise.then,微任务,将其放入微任务队列中
- 遇到同步任务console.log(2),输出2;主线程中同步任务执行完
- 从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中;
- 从微任务队列中取出任务a到主线程中,输出 before timeout;
- 从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中;
- 从微任务队列中取出任务c到主线程中,输出 also before timeout;微任务队列为空
- 从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空
- 从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空
例子3
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(4)
})
p.then(data => {
console.log(data)
})
console.log(3)
//1,3,4,2
- 遇到同步任务console.log(1);输出1;
- 遇到setTimeout 异步宏任务,放入宏任务队列中;
- 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,但由于new Promise没有输出事件,所以接着执行遇到.then;
- 执行.then,异步微任务,被分发到微任务Event Queue中;
- 遇到同步任务console.log(3);输出3;
- 主线程中同步任务执行完,从微任务队列中取出任务到主线程中,p.then 输出4,微任务执行完毕,任务队列为空;
- 开始执行宏任务setTimeout 输出2,宏任务队列为空;
例子4
console.log(1)
setTimeout(function() {
console.log(2)
new Promise(function(resolve) {
console.log(3)
resolve()
}).then(function() {
console.log(4)
})
})
new Promise(function(resolve) {
console.log(5)
resolve()
}).then(function() {
console.log(6)
})
setTimeout(function() {
console.log(7)
new Promise(function(resolve) {
console.log(8)
resolve()
}).then(function() {
console.log(9)
})
})
console.log(10)
//1,5,10,6,2,3,4,7,8,9
- 遇到同步任务console.log(1);输出1;
- 遇到setTimeout 异步宏任务,放入宏任务队列中;
- 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出5,所以接着执行遇到.then;
- 执行.then,异步微任务,被分发到微任务Event Queue中;
- 遇到setTimeout,异步宏任务;放入宏任务队列中;
- 遇到同步任务console.log(10);输出10,主线程中同步任务全部执行完;
- 从微任务队列中取出任务到主线程中,输出6;
- 在从宏任务队列中取出任务到主线程中,执行第一个setTimeout,输出2,3,4(在宏任务中执行同步,同步,异步微任务);
- 在执行第二个setTimeout,输出7,8,9(和8同理);