不要绞尽脑汁的折腾js-EvenLoop(事件循环)了,来看一眼包你学会

149 阅读4分钟

在我们讲解 EvenLoop 之前,我们先来了解一些相关的概念:

进程线程

  1. 进程:CPU 在运行指令和保存上下文所需要的时间 (比如:手机打开微信,系统在执行打开指令,到加载微信上下文环境,直到彻底关闭微信之前的这段时间,都是一个进程)

  2. 线程:是进程中的一个更小的单位,指的是执行一段指令所需的时间 (比如:打开微信聊天界面,就需要一个渲染线程, 同时获取到最新的消息,需要一个网络线程)

浏览器新开一个 tab 页面,就是一个新的进程,这个进程中有很多线程相互配合工作最后展示页面给到用户。这个过程涉及到的线程有:

  1. http 线程
  2. js 引擎线程
  3. 渲染线程

注意:线程之间通常都可以同时工作,但是只有 js 引擎线程和渲染线程是互斥的,但是其他线程之间是可以同时工作的。

异步

  • v8在执行 js 代码时,默认只开启一个线程工作 (js 是单线程的),所以考虑到执行效率,v8 会先执行同步代码,遇到异步代码时,会将异步代码存放到任务队列,等待 js 引擎线程空闲时,再从任务队列中取出异步代码执行。

现在我们正式开始了解EvenLoop

Event-Loop

  • js 代码中有同步和异步之分,异步还被分为宏任务和微任务

微任务:promise.then**(注意:是promise.then 不是promise)**, process.nextTick, MutationObserver

宏任务:setTimeout, setInterval, ajax, setImmediate, I/O, UI rendering

执行顺序:

1. 先执行同步代码(这属于宏任务),这个过程中遇到异步,就存入任务队列

2. 同步执行完毕后,先执行微任务队列中的代码

3. 微任务全部执行完毕后,有需要的情况下渲染页面

4. 渲染完毕后,执行宏任务队列中的代码 (开启了下一次的事件循环)

await

1.会将后续的代码挤入微任务队列

2. 浏览器将 await 的执行时间提前了 (await 后面的代码当成同步来看待)

以下我们来通过几个例子来加强一下事件循环的理解:

1:

在看完上面的执行规则后你可以先尝试下以下的输出情况

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

setTimeout(() => {
  console.log(5);
  setTimeout(() => {
    console.log(6);
  }, 0);
}, 0);

console.log(7);


以下两个函数效果是一样的,只是下面写法麻烦然后官方就重新进行封装然后可以达到一样的效果

image.png image.png

**下图的图片下方和图片上方效果是一样的。得先执行完 a(),才能执行 b() **

image.png

结果为: image.png

我们可以先画张图方面我们后续理解

image.png

首先我们判断同步代码和异步代码(微任务和宏任务),同步代码直接执行,微任务放入微任务队列,宏任务放入宏任务队列(先进先出) `console.log(1);

new Promise((resolve) => {

console.log(2);

resolve(); })和console.log(7); 异步代码微任务放入队列.then(() => { console.log(3);` 宏任务放入队列顺序:

image.png

image.png

image.png

因为代码从上往下运行,遇见时就放入队列,所以是以上顺序。

所以执行完同步,就开始微任务,最后再宏任务就可得出答案

2:

我们可以先自己试着做一下

console.log('script start');
async function async1() {
  await async2()
  console.log('async1 end');
}
async function async2() {
  console.log('async2 end');
}
async1()
setTimeout(() => {
  console.log('setTimeout');
}, 0)
new Promise((resolve, reject) => {
  console.log('promise');
  resolve()
})
  .then(() => {
    console.log('then1');
  })
  .then(() => {
    console.log('then2');
  });
console.log('script end');


我们让代码从上往下执行来看代码。

首先:第一行直接打印(同步) 然后:前面两个函数没被调用不执行直到第9行,await直接执行(上面讲了await的效果)直接调用 async2 所以第六行也就被打印,打印完再去回到第四行(无法打印,被 await效果 害的)放入微任务队列,然后第十行放入宏任务队列,然后14行立马执行(同步),然后后面两个 then 依次放入微任务队列,然后执行23行同步代码,然后再回去执行第一个微任务队列(第4行),其次再后面两个 then 执行,最后再执行最后那个宏任务队列(第十行)

图解

image.png

打印结果:

image.png