性能优化篇 - js事件循环机制(event loop)

前言

以前在作前端的时候,可能注重的东西更偏向于业务层面的东西,切图、实现交互、调用接口等一系列比较浅的技术层,随着前端技术的不断发展,只掌握这些知识是不够的,要学会去了解如何从一个网址,渲染出来一个页面,在到后来可让你看到你想看到的东西,并去操做它,了解浏览器底层的一些渲染机制,是做为一个优秀的前端必不可少的,这篇文章讲解的就是有关js单线程是如何执行的一篇文章,若有不足之处,请指出,我会及时做出改正。javascript


event loop在咱们的平常工做当中,涉及到的地方有不少,只是你们可能不知道这个东西,就是event loop机制致使的这种渲染方式,举个🌰:
console.log(1)
setTimeout(()=>{
    console.log(3)
},1)
console.log(2)
复制代码

显而易见,这个结果就是:一、二、3html

无论setTimeout放在当前做用域的什么位置,结果都是一、二、3,为何,这就是event loop事件循环机制。前端

你们都知道,js是单线程的,若是对多进程和多线程不是特别了解,能够参考这两篇文章:java

浏览器多进程架构promise

浏览器渲染进程多线程浏览器

经过这两篇文章你了解什么是多进程多线程之后,可能看起来这里会好不少。多线程

上面的例子,其实还涉及到了一个问题,就是我setTimeout设置的时间是1ms,这里要注意:架构

w3c在HTML标准中规定,要求setTimeout时间低于4ms的都按4ms来算异步

这里还要注意一点,就是有些时候,为何一些大神用js作一些相似于动画操做的时候,喜欢用setTimeout,而不是用setInterval呢,由于setTimeout是在这个时间后,把当前的方法推到任务队列里,而setInterval是强行把当前的方法添加到任务队列里,这样可能会对当前页面的用户体验很很差,可能会出现效果卡顿的状况,因此这里还要注意一点:函数

setTimeout是延迟执行,可是不是延迟的时间后当即执行的

其实,event loop它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)

js的任务队列分为同步任务和异步任务,全部的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面

主线程

主线程就是简单点的理解,就是访问到的script标签里面包含的内容,或者是直接访问某一个js文件的时候,里面的能够在当前做用域直接执行的全部内容(执行的方法,new出来的对象等),都是在主线程里面作的,添加到任务队列里面的就不算了。仍是老样子,举个🌰:

//index.html
<html>
    <head></head>
    <body>
        <script src="index.js"></script>
        <script>
            console.log(1)
            setTimeout(() => {
               console.log(3) 
            },1)
        </script>
    </body>
</html>

//index.js
console.log(2)
setTimeout(() => {
   console.log(4) 
},1)
复制代码

结果1和2都是在主线程里面执行的,3和4被扔到了任务队列里面。

宏队列(macrotask)

setTimeout、setInterval、setImmediate、I/O、UI rendering

微队列(microtask)

promise.then、process.nextTick、Object.observe(已废弃)


先上代码
console.log(1)
process.nextTick(() => {
  console.log(8)
  setTimeout(() => {
    console.log(9)
  })
})
setTimeout(() => {
  console.log(2)
  new Promise(() => {
    console.log(11)
  })
})
requestIdleCallback(() => {
  console.log(7)
})
let promise = new Promise((resolve,reject) => {
  setTimeout(() => {
    console.log(10)
  })
  resolve()
  console.log(4)
})
fn()
console.log(3)
promise.then(() => {
  console.log(12)
})
function fn(){
  console.log(6)
}
复制代码

结果是一、四、六、三、十二、八、二、十一、十、九、7

这个写法能够囊括80%以上的event loop循环机制的场景了,下面开始梳理具体的运行机制。

js是从上到下执行的,因此上来先打印的是 1 ,继续往下走;

碰见了process.nextTick,由于它属于微队列(microtask),而且当前主线程的代码尚未执行完毕,因此它被展现扔到了微队列里,暂时不打印;

这个时候又遇到了setTimeout,setTimeout是属于宏队列(macrotask);

requestIdleCallback,这里也是不当即执行的,它也不属于任何队列,这里不作详细解释;

promise在实例化的时候,这里的setTimeout继续被丢到了宏队列(macrotask)中,并执行了成功的方法,在有promise.then的调用的时候就会去出发,但这里不作打印,接着发现了console,这里直接打印 4

fn函数直接调用,直接打印 6

console,直接打印 3

promise.then由于它属于微队列,可是它在promise实例化的时候被调用了,因此它会在微队列的最前面执行;

到这里主线程里面就没有任何能够执行到东西了,下面开始走微队列(microtask):

因为promise.then被提早调用了,因此它会先执行,打印 12

微队列(microtask)里面还有一个,就是上面的process.nextTick,执行它,打印 8 ,这个时候发现它有一个setTimeout,放到宏队列(macrotask);

到这里微队列就走完了,下面开始走宏队列(macrotask):

最外面的setTimeout在一开始的时候被放了进去,因此先执行它,打印 2 ,发现它里面有promise被实例化,直接执行,打印 11

下一个要走的就是promise里面的setTimeout,打印 10

还剩最后一个setTimeout,就是process.nextTick里面的,打印 9

到这里主线程、宏队列(macrotask)、微队列(microtask)就全都跑完了,在所有跑完的时候,requestIdleCallback才会执行,打印 7

requesIdleCallback会在当前浏览器空闲时期去依次执行,在整个过程中你可能添加了多个requestIdleCallback,可是都不会执行,只会在空闲时期,去依次根据调用的顺序就执行。

console.log(1)
process.nextTick(() => {
  console.log(8)
  setTimeout(() => {
    console.log(9)
    requestIdleCallback(() => {
      console.log(13)
    })
  })
})
setTimeout(() => {
  console.log(2)
  new Promise(() => {
    console.log(11)
  })
})
requestIdleCallback(() => {
  console.log(7)
})
let promise = new Promise((resolve,reject) => {
  setTimeout(() => {
    console.log(10)
  })
  resolve()
  console.log(4)
  requestIdleCallback(() => {
    console.log(14)
  })
})
fn()
console.log(3)
promise.then(() => {
  console.log(12)
  requestIdleCallback(() => {
    console.log(15)
  })
})
function fn(){
  console.log(6)
  requestIdleCallback(() => {
    console.log(16)
  })
}
复制代码

结果是一、四、六、三、十二、八、二、十一、十、九、七、1四、1六、1五、13

由于这一块不涉及到事件循环里面,可是属于一个比较例外的方法,这里只作简单讲解,不作深刻研究。

总结

其实,event loop用简单点的话去解释,就是:

一、先执行主线程

二、遇到宏队列(macrotask)放到宏队列(macrotask)

三、遇到微队列(microtask)放到微队列(microtask)

四、主线程执行完毕

五、执行微队列(microtask),微队列(microtask)执行完毕

六、执行一次宏队列(macrotask)中的一个任务,执行完毕

七、执行微队列(microtask),执行完毕

八、依次循环。。。

这个过程,其实就是咱们具体要说的js事件循环机制(event loop)。


结束语

这些,是我在网上看一些大神的讲解,和本身对js事件循环机制(event loop)的理解,写的一篇总结性的文章,若是当中有哪些写的不对的地方,还请你们指出,我会在最短期内做出调整。若是以为谢的还行,帮忙给个赞吧。

相关文章
相关标签/搜索