图解JS中的事件循环

图解JS中的事件循环_第1张图片

1、js单线程,并非指js只有一个线程,而是同一时刻只能有一个线程在工作。

2、js中,主线程之外,还有其他线程,比如事件循环线程,定时器触发线程,http异步线程, 浏览器事件线程。

3、在js主线程中,分为两个子线程,js引擎线程,GUI渲染线程。这两个线程是互斥的,同一时刻只能执行一个,要么执行js,要么渲染html

4、任务队列中,分为宏任务微任务。每次执行任务队列时,先执行微任务,再执行宏任务。

5、通常宏任务指 setTimeout,setInterval,XMLHttprequest,fetch等回调。 微任务指 Promise,MutationObserver等回调。

6、如果在 定时器触发线程、http异步线程、浏览器事件线程中,没有回调,则不会放入队列中。

7、事件循环线程,必须等待主线程中的同步代码执行结束,才会去任务队列再取一个 任务放入 主线程中执行。

//index.js
console.log('a');

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

  setTimeout(() => {
    console.log('c');
  }, 0);


  setTimeout(() => {
    console.log('d');

    Promise.resolve().then(() => {
      console.log('e');
    });
  }, 0);


  console.log('f');

下面是上述代码的执行逻辑:

  1. 遇到console.log('a'); 同步代码,执行,输出a;
  2. 遇到Promise,异步代码,放入任务队列。又因为是promise的回调,属于微任务。标记微任务1
  3. 遇到setTimeout执行,放入定制器触发线程中,定时器触发线程中维护何时倒计时结束,并将回调放入任务队列。又因为setTimeout的回调属于宏任务。标记为宏任务1
  4. 又遇到setTimeout执行,放入定制器触发线程中,将回调放入任务队列。因为setTimeout的回调属于宏任务。标记为宏任务2
  5. 遇到console.log('f'); 同步代码,执行,输出f

此时主线程中的同步代码已经完全执行,控制台输出a,f。主线程是空的。此时事件循环线程发现,任务队列有东西,分别是微任务1,宏任务1,宏任务2.

  1. 按照先执行微任务,再执行宏任务顺序,先将微任务1,即 () => { console.log('b'); } 放入主线程中由js执行。输出b,
  2. 此时主线程执行完,又空了,此时任务队列还有宏任务1,宏任务2。由于宏任务1先放入的,按照队列的先进先出顺序。先将宏任务1放入主线程。即 () => { console.log('c'); },输出c,
  3. 再判断队列中是否有微任务,如果有,则全部执行。如果没有,就继续执行宏任务2.
  4. 将宏任务2放入主线程,即

    () => {
     console.log('d');
    
     Promise.resolve().then(() => {
       console.log('e');
     });
    }

    输出d,遇到promise,异步代码,放入微任务队列。标记为微任务2。此时主线程又空了。

  5. 此时任务队列只有微任务2,没有其他的宏任务和微任务。 最后再执行微任务2。即 () => { console.log('e'); }, 输出e

总结下: 最后输出结果为 a f b c d e

注意:
1、上面的 setTimeout(()=>{}); 属于同步代码,会执行,如果 let timer = setTimeout(()=>{}); 你会发现timer有值,是个数字。但也仅仅是执行 setTimeout后将引用返回,剩下的倒计时和回调。都在定时器触发线程中维护。
2、同样,上面的 Promise.resolve() 也属于同步代码,let p = Promise.resolve() .会发现p有值,是个Promise对象,但也仅仅是执行 Promise.resolve() 后将引用返回,剩下的then中的回调。都在微任务队列中维护

以上是自己通过阅读其他文章,加上自己理解和本地调试之后的 一点点感想。 如有问题,还望指正。

你可能感兴趣的:(图解JS中的事件循环)