以下7个demo 如果可以彻底掌握Event Loop面试题,基本上你能遇到的面试题都可以拿下
demo1、await 与微任务
难点:await 后的代码先执行还是外面同步 代码先执行
console.log('aaa');
(async ()=>{
console.log('bbb');
await console.log('ccc');
console.log('eee')
})().then(()=>{
console.log('fff')
});
console.log('ddd');
难点:
1、asyc 中 await 之前算同步还是异步?
2、await 后如果是同步代码 算同步还是异步
3、await 后跟的是同步代码,那 await 还会生效吗
一个结论:await其实等价于then(事实上他俩也确实是一个东西),都是将后续任务放到微任务队列中等待,而不会立即执行
- 第1步、aaa不说了
- 第2步、111是同步执行的,上面说过
- 第3步、222这里很重要了,首先,console.log自己是同步的,所以立即就会执行,我们能直接看到222,但是await本身就是then,所以console.log(333)无法直接执行,而是老老实实去排队,而且,因为整个async并未执行完,它的then(也就是444)无法触发
- 第4步、ddd应该也不用说,当前任务到这里执行完毕
- 第5步、从任务队列中把333拉出来,并且执行了,这时整个async才算完成,所以把then推到队列中等待执行
- 第6步、把console.log(444)拉出来执行,看到444
总结
- Promise构造函数中的代码属于同步,包含 await 后跟的同步代码
- then、catch中的代码才属于 微任务
demo2、宏任务与微任务
难点:微任务内的宏任务先执行还是外面宏任务先执行
console.log('aaa');
setTimeout(()=>console.log('t1'), 0);
(async ()=>{
console.log(111);
await console.log(222);
console.log(333);
setTimeout(()=>console.log('t2'), 0);
})().then(()=>{
console.log(444);
});
console.log('bbb');
- 第1步、毫无悬念aaa,过
- 第2步、t1会放入任务队列等待
- 第3步、111会直接执行,因为async本身不是异步的(上面有说)
- 第4步、222也会直接执行,但是接下来的console.log(333);和setTimeout(()=>console.log('t2'), 0);就塞到微任务队列里等待了
- 第5步、bbb毫无疑问,而且当前任务完成,优先执行微任务队列,也就是console.log(333)开始的那里
- 第6步、执行333,然后定时器t2会加入任务队列等待(此时的任务队列里有t1和t2两个了),并且async完成,所以console.log(444)进入微任务队列等待
- 第7步、优先执行微任务,也就是444,此时所有微任务都完成了
- 第8步、执行剩下的普通任务队列,这时t1和t2才会出来
总结
- 1、先将当前宏任务内的同步代码执行完
- 2、然后执行当前宏任务内的微任务
-
- 遇到宏任务直接丢入宏任务队列
- 3、执行下一个宏任务
demo3 Promise 内嵌 Promise
难点:双层内嵌 Promise 第二个 then 的执行顺序?
setTimeout(() => {
console.log('0');
}, 0)
new Promise((resolve, reject) => {
console.log('1');
resolve();
}).then(() => { // w1
console.log('2');
// return
new Promise((resolve, reject) => {
console.log('3');
resolve(); // w2
}).then(() => { // 📌
console.log('4');
}).then(() => { // w4
console.log('5');
})
}).then(() => {
console.log('6'); //w3 📌
})
new Promise((resolve, reject) => {
console.log('7');
resolve()
}).then(() => {
console.log('8');
})
//[ w1,w2,w3,w4]
//注释为📌的两个then是同层级的,所以按照执行顺序来打印
- 同步代码中遇到 then 则将 then 中的代码(包含 then 后的 then 和 catch)加入微任务队列,后面遇到微任务继续加入队列
-
- 同理宏任务嵌套宏任务也是如此
- 直到当前微(宏)任务执行完
demo4
难点:多次resolve 以哪个为准
new Promise((resolve, reject) => {
console.log('promise');
for (var i = 0; i < 10000; ++i) {
i === 9999 && resolve(i);
}
console.log('promise after for-loop');
}).then((v) => {
console.log('promise1 i:'+v);
}).then(() => {
console.log('promise2');
});
// promise1 i: 会是几?
console.log('start');
var intervalA = setInterval(() => {
console.log('intervalA');
}, 0);
setTimeout(() => {
console.log('timeout');
clearInterval(intervalA);
}, 0);
var intervalB = setInterval(() => {
console.log('intervalB');
}, 0);
var intervalC = setInterval(() => {
console.log('intervalC');
}, 0);
new Promise((resolve, reject) => {
console.log('promise');
for (var i = 0; i < 10000; ++i) {
i === 9999 && resolve();
}
console.log('promise after for-loop');
}).then(() => {
console.log('promise1');
}).then(() => {
console.log('promise2');
clearInterval(intervalB);
});
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('promise in timeout');
resolve();
});
console.log('promise after timeout');
}).then(() => {
console.log('promise4');
}).then(() => {
console.log('promise5');
clearInterval(intervalC);
});
Promise.resolve().then(() => {
console.log('promise3');
});
console.log('end');
- clearInterval(intervalA); 运行的时候,实际上已经执行了 intervalA 的macrotask了\
- promise函数内部是同步处理的,不会放到队列中,放入队列中的是它的then或catch回调\
- promise的then返回的还是promise,所以在输出promise4后,继续检测到后续的then方法,马上放到microtask队列尾部,再继续取出执行,马上输出promise5;
- 提前clearInterval 并不会销毁宏任务,因为当前宏任务已经加入 宏任务队列等待执行
demo5
难点:多个script 内的微任务宏任务 执行顺序??
<script>
console.log('start');
setTimeout(() => {
console.log('timeout1');
}, 0);
Promise.resolve().then(() => {
console.log('promise1');
});
</script>
<script>
setTimeout(() => {
console.log('timeout2');
}, 0);
requestAnimationFrame(() => {
console.log('requestAnimationFrame');
});
Promise.resolve().then(() => {
console.log('promise2');
});
console.log('end');
</script>
demo6 new Promsie 后的throw问题
new Promise((res, rej) => {
console.log(1);
throw new Error('abc'); //抛出错误,下面的代码不执行
res(2);
console.log(3);
}).catch((e) => {
console.log('catch');
}).then((t) => {
console.log(t);
});
变形
new Promise((res, rej) => {
console.log(1);
res(2); //状态发生转变后,后续的错误也无法捕获
throw new Error('abc');
console.log(3);
}).catch((e) => {
console.log('catch');
}).then((t) => {
console.log(t);
});
// 输出 1 --> 2
demo7 new Promise中的await问题
//快手一面
let a;
const b = new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
}).then(() => {
console.log('promise3');
}).then(() => {
console.log('promise4');
});
a = new Promise(async (resolve, reject) => {
console.log(a);
await b;
console.log(a);
console.log('after1');
await a // 后面的代码属于新的微任务,所以,后面非同步代码 且a第一步构造函数内未经历resolve。故a有值状态为pending
resolve(true);
console.log('after2');
}).then(data => {console.info(data)})
console.log('end');
其实以上难点集中在
let a
a = new Promise(async ()=>{
console.log(0)
await a;
console.log(1)
await a;
console.log(2)
})
为什么console.log(2)不会执行呢
首先第一步 await a; 执行时当成同步代码,此时a为undefined,await a; 之后的代码为下一次微任务,此时同步代码执行完毕开始执行 第一个await a 后面的下一次微任务代码 , 第二个 await a;之前无 resolve 所以,第二个await a; 即a为pending状态的Promise,其之后的代码一直无法执行
原题可拆解为
let a;
const b = new Promise((resolve, reject) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
}).then(() => {
console.log('promise3');
}).then(() => {
console.log('promise4');
});
a = new Promise(async (resolve, reject) => {
console.log(a);
b.then(() => {
console.log(a);
console.log('after1');
a.then((resolve)=>{ // a中没有resolve处理所有 a后面的都处于pending状态无法执行
resolve(true);
console.log('after2');
})
})
}).then(data => {console.info(data)})
console.log('end');
以下是一个最简单的demo加深理解
let a = new Promise(()=>{})
new Promise(async (resolve, reject) => {
console.log(0);
resolve(1)
console.log(2);
}).then(()=>{
console.log(3)
})
// 输出 1,2,3
let a = new Promise(()=>{})
new Promise(async (resolve, reject) => {
console.log(0);
await a
resolve(1)
console.log(2);
}).then(()=>{
console.log(3)
})
// 输出 0
为何1,2不会执行,将上面改造如下即可,观察代码可知第二个then 无resolve 和reject故始终保持pending 其后面的then代码始终无法执行
let a = new Promise(()=>{})
new Promise(async (resolve, reject) => {
console.log(0);
}).then(()=>{
console.log(3)
}).then(()=>{
}).then((resolve)=>{
resolve(1)
console.log(2);
}).then(()=>{
console.log(3)
})
// 输出 0
知识点
- 无论是写new Promise 还是直接const a= new Promise ;都会立即执行,
- const a= new Promise 内部 取不到当前值
- 构造函数中无 resolve 则后面的then 不会执行
- 没有 resolve reject 则 不会往下执行
- Promise构造函数内遇到await时,await后的代码看出then中的代码,如果 await之前没有resolve 则该构造函数始终保持pending 而无法走到自己的then状态
参考 zhuanlan.zhihu.com/p/46068171 www.cnblogs.com/thatme/p/10… juejin.cn/post/697381… www.ruanyifeng.com/blog/2013/1… eslint.org/docs/rules/… juejin.cn/post/685041…