浏览器是多线程执行代码,渲染的。但是浏览器只给JS一个线程来执行,因此JS是单线程。因此代码都是同步执行的,但是JS也有异步的任务,比如settimeout,async,await。这些都是异步的任务。因此引出宏任务和微任务,这两个都是异步任务。
- 直接看经典的面试题
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')
- 先看主线程执行,按照代码,从上到下执行。
- 遇到async1的函数,JS创建堆内存。但是没执行函数。继续执行
- 遇到async2的函数,JS创建堆内存。但是没执行函数。继续执行
- 遇到有输出,先输出。
console.log('script start');
。继续执行 - 遇到settimeout,往宏任务添加console.log('setTimeout')的宏任务。继续执行
- 遇到执行async1();找到这个函数的堆内存,执行函数。继续执行
- async1的函数第一步打印出了
console.log('async1 start');
。继续执行。 - 执行 async2的函数
console.log('async2')
;。添加微任务console.log('async1 end'); - new Promise,是立即执行函数,执行了
console.log('promise1');
。添加微任务.then的方法console.log('promise2'); - 输出
console.log('script end')
- 开始evenloop查找微任务。
- 第一个微任务
console.log('async1 end')
- 第二个微任务
console.log('promise2')
- 微任务执行完毕,开始执行宏任务。
console.log('setTimeout')
-
整个逻辑就是,从上到下依次执行。如果遇到同步执行的,就直接执行。遇到宏任务,就添加到宏任务队列,遇到微任务,就添加到微任务队列。同步的执行完毕后,通过evenloop去取微任务,微任务取完毕后,才开始取宏任务。
流程图
第二道示范代码,注意宏任务跟微任务都要执行的时候,微任务优先。
setTimeout(() => {
console.log("time1");
setTimeout(() => {
console.log("time3");
}, 0);
new Promise((resolve) => {
resolve();
console.log("new resolve");
}).then(() => {
console.log("Promise then");
});
}, 0);
setTimeout(() => {
console.log("time2");
}, 0);
console.log("mian");
- 代码从上到下执行。
- 遇到setTimeout,把里面的代码块console.log("time1");等等之类的,丢到宏任务。
- 遇到setTimeout,把console.log("time2");丢到宏任务。
-
console.log("mian");
主线程执行完毕。 - 开始执行第一个宏任务。
console.log("time1");
- 遇到setTimeout,把console.log("time3");丢到宏任务。
- 遇到new Promise,直接执行。
console.log("new resolve");
。并且把.then后面的console.log("Promise then");扔到微任务。 - 主线程执行完毕。
- 这个时候有微任务和console.log("time2");的宏任务。按照优先级,执行微任务。因此先
console.log("Promise then");
- 微任务都执行完毕,取宏任务。
console.log("time2");
- 在再次查找,微任务没有,剩下最后一个宏任务。
console.log("time3");
留下题目
console.log('==== start ====')
setTimeout(function () {
console.log('定时器')
setTimeout(function () {
console.log('定时器中的定时器')
}, 0)
new Promise(function (resolve) {
console.log('准备执行 for 循环111')
for (let i = 0; i < 666; i++) {
i == 5 && resolve()
}
}).then(function () {
console.log('执行了 then 方法1111')
})
}, 0)
console.log('==== end ====')
new Promise(function (resolve) {
console.log('准备执行 for 循环222')
for (let i = 0; i < 666; i++) {
i == 5 && resolve()
}
}).then(function () {
console.log('执行了 then 方法2222')
})
不能被resolve混淆了。先执行first()。然后才是then进入微任务。因此first()方法里面的p.then微任务更先进。
const first = () => (
new Promise((resolve, reject) => {
console.log(3);
resolve(2);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(()=>{
console.log(5);
resolve(6);
}, 0)
resolve(1);
})
p.then((arg) => {
console.log(arg);
})
})
)
first().then((arg)=>{
console.log(arg);
})
console.log(4);
网友评论