一、宏任务、微任务
参考文章:什么是宏任务、微任务?宏任务、微任务有哪些?又是怎么执行的?-CSDN博客
a.二者对比
b.事件循环
1>宏任务中有微任务
- 宏任务(例如:
setTimeout、I/O 操作、UI 渲染等)会首先执行。 - 在执行宏任务的过程中,如果有微任务被创建(例如在宏任务中执行
Promise.resolve()或queueMicrotask()),这些微任务会被添加到 微任务队列。 - 在当前的宏任务执行完之后,所有的微任务会在下一个宏任务执行之前依次执行,直到微任务队列为空。
- 然后,再开始执行下一个宏任务。
console.log("Start");
setTimeout(() => {
console.log("Macro task 1");
// 在宏任务中创建微任务
Promise.resolve().then(() => {
console.log("Microtask inside Macro task 1");
});
console.log("End of Macro task 1");
}, 0);
setTimeout(() => {
console.log("Macro task 2");
}, 0);
console.log("End");
输出结果:---->Start / End / Macro task 1 / End of Macro task 1 / Microtask inside Macro task 1 / Macro task 2
2>宏任务中有宏任务
console.log("Start");
setTimeout(() => {
console.log("Macro task 1");
Promise.resolve().then(() => {
console.log("Microtask inside Macro task 1");
// 创建一个宏任务
setTimeout(() => {
console.log("Macro task 1.1");
}, 0);
// 创建另一个微任务
Promise.resolve().then(() => {
console.log("Microtask 1.1");
});
// 再次创建宏任务
setTimeout(() => {
console.log("Macro task 1.2");
}, 0);
});
console.log("End of Macro task 1");
}, 0);
setTimeout(() => {
console.log("Macro task 2");
// 在宏任务 2 中创建微任务
Promise.resolve().then(() => {
console.log("Microtask inside Macro task 2");
// 嵌套一个宏任务
setTimeout(() => {
console.log("Macro task 2.1");
}, 0);
});
console.log("End of Macro task 2");
}, 0);
setTimeout(() => {
console.log("Macro task 3");
}, 0);
console.log("End");
3>混合示例
console.log("Start");
setTimeout(() => {
console.log("Macro task 1");
// 在宏任务 1 中使用 process.nextTick
process.nextTick(() => {
console.log("process.nextTick inside Macro task 1");
});
}, 0);
setTimeout(() => {
console.log("Macro task 2");
// 在宏任务 2 中创建微任务
Promise.resolve().then(() => {
console.log("Microtask inside Macro task 2");
// 在微任务中创建新的宏任务
setTimeout(() => {
console.log("Macro task 2.1");
}, 0);
});
}, 0);
// 在外部创建微任务
Promise.resolve().then(() => {
console.log("Microtask 1");
// 在微任务 1 中创建新的微任务
Promise.resolve().then(() => {
console.log("Microtask 1.1");
// 在微任务 1.1 中创建宏任务
setTimeout(() => {
console.log("Macro task 3");
}, 0);
});
// 使用 async/await 创建微任务
(async () => {
console.log("Async/Await in Microtask 1");
await Promise.resolve();
console.log("After await in Microtask 1");
})();
});
// 使用 async/await 创建宏任务
//异步立即执行函数
(async () => {
console.log("Async/Await in Macro task");
await Promise.resolve();
console.log("After await in Macro task");
})();
console.log("End");
4>总结
-
执行同步任务(立即执行函数等),然后遇到宏任务添加到宏任务队列,遇到微任务添加到微任务队列。
-
在执行宏任务之前,微任务队列会被清空。这意味着每次执行宏任务时,都会先执行完所有的微任务。
-
执行微任务队列时,如果遇到宏任务,会将其添加到宏任务队列,遇到微任务则会继续添加到微任务队列。这个过程会一直持续,直到微任务队列为空。
-
执行宏任务队列:
执行宏任务队列中的任务。每次从宏任务队列中取出一个任务执行。
执行过程中如果遇到 宏任务,则继续将其加入宏任务队列;遇到 微任务,则将其加入微任务队列中。
每次执行完一个宏任务后,会去执行微任务队列中的所有任务。然后继续执行下一个宏任务,重复3,直到宏任务队列为空。
c.接下来我们随便玩几个栗子叭
./one
console.log('1');
setTimeout(function () {
console.log('2');
new Promise(function (resolve) {
console.log('4');
resolve();
}).then(function () {
console.log('5')
})
})
new Promise(function (resolve) {
console.log('7');
resolve();
}).then(function () {
console.log('8')
})
setTimeout(function () {
console.log('9');
new Promise(function (resolve) {
console.log('11');
resolve();
}).then(function () {
console.log('12')
})
})
// 结果:1 7 8 2 4 5 9 11 12
./two
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);
./three
接下来我们对同一个代码片段进行改动,来判断输出结果,(我将对主要修改地方进行说明)
async function fn1() {
console.log(1)
let res = await fn2()
console.log(res)
console.log(2)
}
async function fn2() {
//同步任务
return new Promise(() => { console.log("fn2") })
}
fn1()
console.log(3)
// 1 fn2 3
//此时不打印2,是因为fn1 返回了一个永远没有解决的promise!!!!!!!!!!,console.log(2)执行任务被阻塞
//哇塞是promise我们有救了
我们进行改动
async function fn1() {
console.log(1)
let res = await fn2()
console.log(res)
console.log(2)
}
async function fn2() {
return Promise.resolve("fn2")
}
fn1()
console.log(3)
//1 3 fn2 2
再来改动
async function fn1() {
console.log(1)
let res = await fn2()
console.log(res)
console.log(2)
}
async function fn2() {
//同步任务
setTimeout(() => {
console.log("fn2")
})
}
fn1()
console.log(3)
// 1 3 2 fn2
大灰狼请注意---->async 和 Promise 之间的关系
-
async函数会自动将函数的返回值包装成Promise。无论你在async函数中是直接返回一个值,还是返回一个Promise,async函数都会将其包装成Promise,并且它会在执行完成后根据返回值的类型,决定是否将这个Promise的状态设置为已解决(fulfilled)或已拒绝(rejected)。- 如果
async函数内有return,return的值会被自动封装在一个已解决(fulfilled)的Promise中。 - 如果
async函数内发生异常,则会返回一个已拒绝(rejected)的Promise,异常作为拒绝的原因。
- 如果
./four
function block(delay) {
const start = Date.now()
while (Date.now() - start < delay) { window.timestamp = Date.now() }
}
function a() {
console.log(1)
block(1000)//同步阻塞
setTimeout(() => console.log(2), 0)
setTimeout(() => console.log(3), 1)
}
function b() {
console.log(4)
}
console.log(5)
setTimeout(a(), 0)
setTimeout(b(), 500)
// 5 1 4 2 3
我们对其中一部分代码进行修改
function block(delay) {
const start = Date.now()
while (Date.now() - start < delay) { window.timestamp = Date.now() }
}
function a() {
console.log(1)
block(1000)//同步阻塞
setTimeout(() => console.log(2), 0)
setTimeout(() => console.log(3), 1)
}
function b() {
console.log(4)
}
console.log(5)
setTimeout(a, 0)!!!!!!!注意,这里进行了修改
setTimeout(b, 500)
// 5 1 2 4 3
//多个宏任务(如 setTimeout)的执行顺序与它们的第二个参数(即延迟时间)是相关的,但是它并不是唯一的决定因素
最后一个,太棒了,马上就到终点了!撒花!!!
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end' )
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log('async2')
}
async1()
setTimeout(() => {
console.log('timer3')
}, 0)
console.log('start')