EventLoop & Promise & async/await 中的同步异步机制

134 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

EventLoop 事件循环机制

浏览器是多线程,但是它只分配一个“JS引擎线程”用来渲染和解析JS代码,所以JS是单线程的!!

  • JS中大部分代码都是“同步编程”,例如:循环...
    • 千万不要写死循环,一旦遇到死循环,则JS引擎线程会一直被占用,其它事情都做不
    • 遇到程序抛出异常,后面的代码也不会再执行
      • 我们可以基于 try/catch 进行异常捕获,这样不会影响后续代码的执行
  • JS中也存在“异步编程”:依托于浏览器多线程,再基于EventLoop事件循环机制处理的
    • 异步宏任务 macrotask
      • 定时器 setTimeout/setInterval
      • 事件绑定
      • ajax/fetch
      • ...
    • 异步微任务 microtask
      • requestAnimationFrame
      • Promise.then/catch/finally
      • async await
      • queueMicrotask 基于这个方法可以创建一个异步微任务
      • IntersectionObserver
      • ... 定时器到时间后不一定能执行(设定的时间是其最快执行的时间):如果此时主线程被占用,则必须等主线程空闲下来,排在EventQueue中的定时器才可以执行!
  1. 基于JS和定时器实现动画效果,存在以下问题:
  • 出现卡顿的状况:到时间该走了,但是主线程被占用,它走不了
  • 我们设定的时间很难和“屏幕刷新率”保持一致
  1. 但是可以基于window.requestAnimationFrame实现动画
  • 不需要我们设置时间,默认是按照电脑的“屏幕刷新率对应的时间”进行运动的

  • 也会出现“因主线程被占用,它无法立即执行”导致的卡顿,但是比定时器好,因为它是异步微任务,优先于异步宏任务执行

EventLoop.png

Promise & async/await 中的同步异步机制

executor函数会被立即执行[同步]:这里一般是管理异步编程代码,当异步结束,基于resolve/reject执行,控制实例状态的成功和失败...

new Promise(resolve => {
  console.log(1);
});
console.log(2);
// 1
// 2

resolve/reject执行:

  • 立即修改实例状态和值[同步] p1.then(onfulfilled,onrejected)
  • 如果此时已知p1实例的状态(不是pending),会根据状态去执行onfulfilled/onrejected;
    • 但并不会立即执行,而是创建一个“异步的微任务”
    • 进入到WebAPI中去监听,但是此时已经知道状态是成功的,则直接挪至EventQueue任务队列中排队
    • 当同步代码执行完,主线程空闲下来了,再去EventQueue任务队列中查找
let p1 = new Promise(resolve => {
  resolve(100); 
})
console.log(p1); // fulfilled & 100
p1.then(value =>{
  console.log('成功',value);
},reason => {
  console.log('失败',reason)
})

执行then的时候,此时不知道p1实例的状态

  • 把 onfulfilled & onrejected 存储到promise内部的集合中
let p1 = new Promise(resolve => {
  /*
  立即修改实例的状态和值[同步]
  通知之前在集合中存储的方法执行
    + 把存储的方法扔到WebAPI中去监听,但是发现实例状态已经是成功了,则挪至到EventQueue任务队列中配对等着
    + 把此上下文中剩下的代码执行完,再去获取这个异步任务执行
  */
  set Timeout(() => {
    resolve(100)
    console.log(222,p1);
  })
})

p1.then(value => {
  console.log('成功',value);
});
console.log(111);
// 111
// 222,100
//成功,100

遇到await

  • 把await后面的东西进行处理[同步],获取一个promise实例
  • 然后把当前上下文中,await下面的代码,设置为“异步的微任务”
    • 进入WebAPI监听,当await后面的实例是成功的,再挪至EventQueue任务队列中排队等着
(async () => {
  let num = await Promise.resolve(100);
  console.log(num)
})()
console.log(11);

//11  100