JS事件循环机制-eventLoop

272 阅读4分钟

「这是我参与11月更文挑战的第25天,活动详情查看:2021最后一次更文挑战」。

JS事件循环机制

进程和线程

  • 浏览器打开一个页面就相当于开一个进程,在进程中,我们会同时做很多事情,每一个事情都有一个“线程”去处理,所以一个进程中可能会包含多个线程!!
  • 浏览器是多线程的: GUI渲染线程:渲染页面 & 绘制图形 JS引擎线程:渲染和解析JS代码 事件触发线程:监听事件触发 定时触发器线程:给定时器计时的 异步HTTP请求线程:基于HTTP网络从服务器端获取资源和信息 WebWorker ...
  • 同时做多件事情是“异步编程”; 一次只能处理一件事情,上一件事情处理完,下一件才能开始处理,这种操作是“同步编程” 异步编程实现的机制
    • 多线程机制
    • EventLoop事件循环机制
    • ...
  • JS是单线程的(浏览器只分配一个线程“JS引擎线程”用来渲染和解析JS代码)
    • JS中的大部分代码操作都是“同步”

    • 有少部分操作,结合Eventloop机制,实现了“异步”处理

      [异步宏任务:macrotask]

      • 定时器:setTimeout/setInterval
      • 事件绑定/队列
      • 数据请求:Ajax/Fetch
      • MessageChannel
      • setImmediate[NODE]
      • ... [异步微任务:microtask]
      • Promise.then/catch/finally
      • async/await
      • queueMicrotask
      • MutationObserver
      • IntersectionObserver
      • requestAnimationFrame
      • process.nextTick[NODE]
      • ...

JS事件循环解析

console.log(1);
setTimeout(() => {
    console.log(2);
}, 0);
console.log(3);
for (let i = 0; i < 99999999; i++) {} //100MS左右
console.log(4);

JS事件循环-1.png

setTimeout(() => {
    console.log(1);
}, 20);
console.log(2);
setTimeout(() => {
    console.log(3);
}, 10);
console.log(4);
for (let i = 0; i < 90000000; i++) {}
console.log(5);
setTimeout(() => {
    console.log(6);
}, 8);
console.log(7);
setTimeout(() => {
    console.log(8);
}, 15);
console.log(9);

JS事件循环-2.png

promise的使用总结

let p1 = new Promise((resolve, reject) => {
  console.log(1)
  resolve(100)
  console.log(2)
})
console.log(p1) //fulfilled 100
//p1.then 如果已知promise实例的状态,我们就把执行的方法onfulfilled或者onrejected放在EventQueue的异步微任务队列中等待执行[并不会立即执行方法,也即是then其实是异步的任务]




let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(100) //立即更新实例的状态和 值,但是通知之前存储的方法执行这个操作是“异步”的
    console.log(2)
  },1000)
})
console.log(p1) //pending
//如果此时还不知道p1实例的状态,则我们会把onfulfilled/onrejected存储在一个容器中,等到后期我们基于resolve/reject把实例状态修改后,则在通知之前存放的方法执行【但是也不是立即执行,而是放在异步的微任务等待队列中】
 p1.then(result => {
    console.log(`成功:${result}`);
}, reason => {
    console.log(`失败:${reason}`);
});
console.log(3);
// 3  2  成功:100 




// 基于then返回的promise实例,它的状态和值,主要看onfulfilled/onrejected执行
//   - 函数返回的不是promise实例:方法执行不报错,p2状态是成功,值是返回值;方法执行报错,则p2是失败的,值是报错原因
//   - 函数返回的是个promise实例:则这个实例的状态和值决定了p2的状态和值

let p1 = Promise.resolve(100);
console.log(p1); //fulfilled
let p2 = p1.then(result => { //p1.then的时候,此处的onfulfilled方法放在EventQueue中等待执行(@A)
    console.log(`成功:${result}`);
    return 1000;
});
console.log(p2); //pending
p2.then(result => { //此时还不知道p2的状态,所以先把onfulfilled存储起来(@B)
    console.log(`成功:${result}`);
});
console.log('SYNC END');
//同步结束后开始执行@A -> 成功:100并且修改p2的状态是成功,值是1000;
//此时才知道@B可以执行了,把其也放在等待的异步微任务队列中 -> 如果没有其他的异步任务执行,这把@B也拿出来执行 -> 成功:1000

async/await使用总结

const query = () => {
  return new Promise(resolve => {
    setTimeout(() => resolve(300),1000)
  })
}
(async () => {
  //如果await后面放置的不是一个Promise实例,则浏览器默认会把其转换为一个“状态为成功,值就是await后的值”的promise实例
  //  - await需要等待后面的promise实例是状态为成功的,才会执行下面的代码
  //  - 首先当前上下文中,await下面的代码都是异步的微任务  @aw
  //  - 如果已经知道await后面的实例状态是成功的,则 @aw 直接放在EventQueue中,等待执行即可
  //  - 如果后面实例状态是失败的,则 @aw 在webapi中永远不会进入到EventQueue中,因为永远不会执行
  //  - 如果暂时还不知道是成功还是失败,则@aw 先放置在webapi中,等到知道实例状态是成功后,在挪至到EventQueue中等待执行
  let result = await 1 //await Promise.resolve(1)
  console.log(result);

  result = await Promise.resolve(2)
  console.log(result)

  result = await querey();
  // 先把query执行,把返回的promise实例放在await后面等着,当前案例只有1000ms后,才能知道实例状态
})()