JS的事件执行机制详细了解

98 阅读3分钟

为什么js是一个单线程

js的主要一个用途就是操作dom。所以,如果js有两个线程,同时去操作同一个dom的话,浏览器并不知道该听哪个线程的,也无法判断两个的优先级。所以,js只能是一个单线程的语言。

js的事件执行机制

所以问题来了,如果js同时加载很多的资源,难道要等这些资源一个一个加载完,页面才显示吗,这样,用户体验就会很差,所以,js是通过同步和异步操作完成多线程的效果的。

js的事件循环

js的事件循环分为主线程任务队列。主线程负责同步任务,而任务队列负责异步任务。

事件循环的流程大致是:

1、 浏览器优先读取主线程(一般为script)的代码。 2、 当主线程的任务为空时,会去任务队列中触发当前该触发的任务。 3、 这个过程会不断重复,就形成了事件循环(Event Loop)

异步任务

//demo
console.log('Start');

setTimeout(() => {
  console.log('Timeout callback');
}, 1000);

console.log('End');

复制代码

在这个例子中,浏览器会优先执行script的部分,console.log('Start'),而由于setTimeout是一个异步任务,他会在一秒后,将函数推入任务队列,而主线程并不会等待这个任务,会先去执行console.log('End')。当检查完主线程的同步任务为空时,再去查找任务队列中是否有需要执行的任务,再去执行。

宏任务与微任务

异步任务又分为宏任务和微任务。

浏览器会优先读取主线程(一般为script)的代码。主线程任务处理完后,会处理任务队列中的微任务,微任务全部处理完成,再去处理宏任务。接着再去处理下一个微任务...,再去处理下一个宏任务...

宏任务一般有:setTimeout、setInterVal等。 微任务一般有:Promise.then catch finally、process.nextTick。

举个例子:

       setTimeout(function () {
        console.log(1)
      })
​
      new Promise(function (resolve, reject) {
        console.log(2)
        resolve(3)
      }).then(function (val) {
        console.log(val) // 微任务1
      })
​
      new Promise(function (resolve, reject) {
        console.log(4)
        resolve(5)
      }).then(function (val) {
        console.log(val) // 微任务2
      })
​
      console.log(6)

复制代码

这段代码的输出应该是2 4 6 3 5 1

解释:浏览器优先处理主进程的任务,为第一个Promise的console.log(2)、第二个Promise的console.log(4),和最外层的console.log(6)。在处理完主线程后,会查找任务队列的任务,优先查找到任务队列的微任务,resolve(3)和resolve(5),最后当任务队列中的微任务处理完成后,再处理宏任务setTimeout。

这样一个代码的事件循环就完成了,所以输出答案为‘2、4、6、3、4、1’。

总结:

  • 由于js需要操作dom,所以js只能为单线程语言。
  • js的事件循环由主线程(同步)和任务队列(异步)组成,而任务队列中包含宏任务、微任务。
  • 循环机制:执行主进程 ---> 所有微任务 ---> 宏任务A ---> 所有 新产生的 微任务 ---> 宏任务B