JS 事件轮询总结---做完题目真的学会了

61 阅读5分钟

相关概念:

  1. 进程-线程 作为理解
  2. 全局同步任务--事件队列中的异步代码
  3. 事件队列中有:宏任务与微任务

事件轮询图

image.png

事件轮询过程

  1. 首先是执行同步代码
    1. 如果遇到 setTimeout
      1. 0s 延迟就将 setTimeout 加入宏任务队列中
      2. 如果有延迟就等待时间到达再加入到宏任务队列中
      3. 注意细节:只要执行到了 setTimeout 就开始计时,时间到达了就加入宏任务队列
      4. 注意细节:如果在不同时间经过了多个 setTimeout,那么大家都一起在计时,只要谁的时间到了就加入到 宏任务队列中
      5. 注意细节:setTimeout 的计时操作是 浏览器开启了一个新的线程进行计时,与 js 无关(那么这个事件队列又是谁创建的呢?值得思考)
    2. 如果遇到 Promise
      1. 首先 promise 中的代码是会直接执行的,遇到 resolve 或者 reject 才算是执行完毕
      2. 当 遇到 resolve 或者 reject 表示 当前 promise 状态已经改变,他后面跟着的 .then 所有的代码会加入到 微任务队列中
    3. 其他同步代码继续执行,直到全部执行完毕
    4. 总结:同步代码的执行就做两件事:
      1. 执行同步代码
      2. 将异步代码放到事件队列中
  2. 同步代码执行完毕后就执行事件队列中的代码
    1. 首先会执行微任务队列中的任务,直到微任务队列清空(我理解的就是将微任务队列中的任务一个一个的加入到 JS 线程中按序执行)
    2. 如果微任务中又添加了一个微任务,那这个任务也会被加入到微任务队列中,按顺序执行(意思就是微任务队列就一个,添加任务都是加到这一个队列里面,不会因为轮次的不同而创建多个微任务队列)
    3. 注意:尽量不要在微任务中又产生微任务,这样子会造成宏任务队列的堵塞
  3. 微任务队列中的代码执行完毕之后就会执行宏任务队列的代码
    1. 首先执行宏任务队列中队头的代码(我理解的就是将宏任务对头的任务加入到 JS 线程中)
      1. 如果产生了宏任务,那就添加到宏任务队列中
      2. 如果产生了微任务,那就添加到微任务队列中
    2. 当执行完一个宏任务之后,从 JS 线程回到 事件队列中
      1. 这就是事件轮询的第二次循环了,会查看微任务队列是否有任务
        1. 如果有的话那就得先清空微任务队列中的任务,也就是先执行微任务队列中的代码
        2. 清空了微任务队列后再将宏任务队头的代码加入到 JS 线程中
    3. 总结:宏任务队列中的任务,执行完一个之后就会再去看看微任务队列是否被清空!!!
  4. Async 与 Await 中的执行顺序
    1. 当遇到 await 标识的代码时,它右侧的代码是一个 Promise ,会同步执行内部的代码(其实就是在执行Promise代码)

      1. await 会等待右侧的 promise 的状态改变为 fulfilled ,也就是 resolve()之后继续执行后面的代码
      2. 如果右侧的状态改为 fail,也就是 reject(),那么 await 后面的代码都不会被执行了,会抛出一个错误等待被捕获,如果没有被捕获那就会被浏览器捕获**(好像promise不写一个catch会报错的,提示对异常进行捕获)**
      3. 注意:如果右侧的 promise 执行完毕都没有执行 resolve()或者 reject(),那么其实是默认执行 resolve(undefined),也就是说状态也会变成 fulfilled
    2. 这其实考察的是我们对于 await 的理解

      1. promise.then 是去执行 resolve 后的代码,而 promise.catch 会执行 reject 后的代码
      2. await 是在等待 promise 的状态变为 fulfilled,那这不就是类似于 then 方法吗?
        1. 所以 await 实际上使用的就是 then 回调
        2. 而多个 await 如果用 then 来写的话 会形成很多个 嵌套代码,使用 await 就看着像同步代码一样了
promise.then(res=>{

}).then(res=>{

}).then(res=>{})
类似于:
p1 = await promise1
p2 = await promise2
     3.   AsyncAwait 实际上就是 promise 和 生成器的语法糖
        1. 生成器: yield 也是会执行完右侧代码后中断代码,等待 next()函数的调用
        2. yield 实际上就是将迭代器的过程进行了一次封装,不需要手动去配置迭代器函数了,内置 nextreturn 等方法

题目测试:

提示:题目有些难,注意自己画图推演,掌握以上的知识之后做题基本没问题了!!!加油!!!


题目一:

setTimeout(function () {
  console.log('setTimeout1');

  new Promise(function (resolve) {
    resolve(undefined)
  }).then(function () {
    new Promise(function (resolve) {
      resolve(undefined)
    }).then(function () {
      console.log('then4');
    })
    console.log('then2');
  })
})

new Promise(function (resolve) {
  console.log('promise1');
  resolve(undefined)
}).then(function () {
  console.log('then1');
})

setTimeout(function () {
  console.log('setTimeout2');
})

console.log(2);

queueMicrotask(() => {
  console.log('queueMicrotask1');
})

new Promise(function (resolve) {
  resolve(undefined)
}).then(function () {
  console.log('then3');
})

执行结果与画图理解: image.png image.png


题目二:

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(undefined)
}).then(function () {
  console.log('promise2');

})

console.log('script end');


执行结果与画图理解: image.png

声明

  • 这篇文章是自己通过 coderwhy 老师的授课自己做的一篇总结,例题也是从 老师 那里拿的(非常具有代表性!!!)
  • coderwhy 老师讲的 JS高级 搭配 小红书 一起看吸收真的超级大的,相当有横向把JS知识往下挖了一层,也为我提供了局部深入理解的方向!
  • 学习思路的总结以及图片是自己画的,还有待提升,大家看看就好 哈哈