异步执行题目

164 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

异步执行的函数主要考察的是要对宏任务和微任务进行详细的了解,这小文章介绍了宏任务和微任务的执行的思路。 如果喜欢(❤ ω ❤)点个赞吧!

概念

image.png

console.log('hi')
setTimeout(function(){
console.log('cbl')
},1000)
console.log('bye')

在JS引擎和Node环境中存在着一个事件轮询的实践,在Call Stack中是宏任务所决定的执行的顺序,当遇到异步执行的事件时就会产生宏任务事件。注意: 在刚开始声明时整个js代码刚开始就是一个宏任务,是进入事件轮询的基础。其中微任务是宏任务的一个子集,比如在steTimeout事件中,可能存在着微任务,当执行一个宏任务时,在同步任务执行完成之后,就要检查是否在这个宏任务中存在微任务,当当前的宏任务执行完成之后才会去执行下一个宏任务。
执行的顺序为:同步任务--->微任务--->下一个宏任务

分类

宏任务:

  1. I/O
  2. setTimeout
  3. setInterval
  4. setImmediate
  5. requestAnimatonFrame

微任务:

  1. process.nextTick
  2. MutationObserver
  3. Promise.then catch finally

练习题

1

在Promise中除了resovle和reject调用, 其他的代码操作都是同步任务。Promise的状态只能改变一次,并且在链式调用then时就会对前一次调用的就结果进行依赖,并且then方法返回的也是一个Promise对象

const promise = new Promise((resolve,reject)=>{
    console.log(1);
    resolve();
    console.log(2);
    reject()
})
setTimeout(()=>{console.log(5)},0)
promise.then(()=>{console.log(3)})
.then(()=>{console.log(6)})
.catch(()=>{console.log(7)})
console.log(4)

答案是1,2,4,3,6,5
注解: 先执行同步代码,在执行微任务对列,在执行宏任务队列。promise.then属于微任务,setTimeout属于宏任务。另外promise状态只改变一次,所以catch不执行。

首先new Promise时候打印1和2,因为new Promise时候会立即执行传入的方法 然后后面代码都是异步代码,先将setTimeout的回调加入宏任务队列,再把promise.then放入到微任务队列,然后直接执行最后一句,打印4。 这样宏任务代码执行完了,接下来开始执行微任务队列中的任务,由于promise resolve,因为promise resolve之后状态不会再改变,因此不会执行到reject的对调,所以打印3和6 微任务队列为空,再到宏任务队列中查找任务,找到setTimeout回调执行,打印5 调用栈、宏任务队列、微任务队列都为空,代码执行结束。

2

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve();
        }, 0);
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));
first().then((arg) => {
    console.log(arg);
});
console.log(4);

参考答案:3, 7, 4, 1, 2, 5

  1. 首先定义first
  2. 然后执行first,然后执行new Promise传入的方法,先打印3
  3. 又new Promise,执行其中传入的方法,打印7
  4. 执行setTimeout,将回调放入宏任务队列
  5. 执行resolve(1),将内部promise状态置为fullfilled,值为1
  6. 执行resolve(2),将外部promise状态置为fullfilled,值为2
  7. 执行内部promise.then方法,将回调加入微任务队列
  8. 执行first().then,即外部的promise,将回调加入到微任务队列
  9. 调用栈为空,开始从微任务队列拿取任务,首先拿到内部promise的回调,打印其值1
  10. 然后从微任务队列中拿取外部的promise的回调,打印其值2
  11. 此时微任务队列为空,开始从宏任务队列中拿取任务,即setTimeout回调,打印5。
  12. 调用栈,宏任务队列和微任务队列都为空,执行结束。

3

console.log(1);
new Promise(resolve => {
    resolve();
    console.log(2);
}).then(() => {
    console.log(3);
})
setTimeout(() => {
    console.log(4);
}, 0);
console.log(5);

参考答案:1,2,5,3,4

  1. 先打印1
  2. 执行new Promise的函数,打印2
  3. 执行promise.then,将回调加入微任务队列
  4. 将setTimeout的回调加入宏任务队列
  5. 打印5
  6. 调用栈为空,取微任务队列中的任务执行,打印3
  7. 微任务队列为空,取宏任务队列任务执行,打印5
  8. 调用栈、微任务队列、宏任务队列都为空,执行结束

4

Promise.resolve()
.then(() => {
    console.log('1');
})
.then(() => {
    console.log('2');
});


setTimeout(() => {
    Promise.resolve()
    .then(() => {
        console.log('3');
    })
    .then(() => {
        console.log('4');
    });
    setInterval(() => {
        console.log('5');
    }, 3000);
    console.log('6');
}, 0);

参考答案:1,2,6,3,4,5,5...

  1. 先执行Promise.resolve,将两个回调加入到微任务队列中
  2. 执行setTimeout,将其回调加入宏任务队列
  3. 调用栈为空,拿出微任务队列中的两个回调执行,打印1,2
  4. 微任务队列为空,拿出宏任务队列中的setTimeout的回调执行
  5. 将setTimeout中的Promise.resolve的两个回调加入到微任务队列
  6. 将setTimeout中的setInterval的回调加入宏任务队列
  7. 打印6
  8. 取出微任务队列中的两个Promise的回调,打印3,4
  9. 取宏任务队列中的setInterval的回调执行,每隔3s符合执行条件,打印5。注意setInterval调用时候不马上会执行一次,第一次执行是3s以后。

5

setTimeout(function() {
    console.log(1);
}, 0);
console.log(2);
async function s1() {
    console.log(7)
    await s2();
    console.log(8);
}
asycn function s2() {
    console.log(9);
}
s1();
new Promise((resolve, reject) => {
    console.log(3);
    resolve();
    console.log(6);
}).then(() => console.log(4))
console.log(5);

参考答案:2,7,9,3,6,5,8,4,1

  1. 记住async只是promise的语法糖,转化为等价的形式就好分析了
  2. 先执行setTimeout,加入宏任务队列中
  3. 打印2
  4. 执行s1,同步打印7
  5. 执行s2,同步打印9
  6. 执行完s2,将console.log(8)加入到微任务队列
  7. 然后执行s1后面的Promise,打印3和6
  8. 执行then,将console.log(4)加入到微任务队列中
  9. 打印5
  10. 调用栈为空,将微任务队列中的两个任务依次拿出来执行,打印8和4
  11. 微任务队列执行完,将宏任务队列的任务拿出来执行,打印1
  12. 调用栈、微任务队列、宏任务队列都为空,执行完毕。

6


<script> 
setTimeout(function () {//宏任务1
      console.log('1');
    });
    new Promise(function (resolve) {
      console.log('2');//同步任务1
      resolve();
    }).then(function () {//微任务1
      console.log('3');
    });
    console.log('4');//同步任务2
    setTimeout(function () {//宏任务2
      console.log('5');//宏任务2中的同步任务
      new Promise(function (resolve) {
        console.log('6');//宏任务2中的同步任务
        new Promise(function (resolve) {//宏任务2中的微任务
            console.log('x1');
            resolve();
          }).then(function () {
            console.log('X2');
          });
        setTimeout(function () {//宏任务2中的宏任务
          console.log('X3');
          new Promise(function (resolve) {//宏任务2中的宏任务中的同步任务
            console.log('X4');
            resolve();
          }).then(function () {//宏任务2中的宏任务中的微任务
            console.log('X5');
          });
        })
        resolve();
      }).then(function () {//宏任务2中的微任务
        console.log('7');
      });
    })
    setTimeout(function () {//宏任务3
      console.log('8');
    });
    //(对于这段代码node环境和浏览器环境输出一致)
    //输出答案:2,4,3,1,5,6,x1,x2,7,8,x3,x4,x5
</script>