js事件循环机制(event loop)

栏目: JavaScript · 发布时间: 5年前

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

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

event loop在我们的日常工作当中,涉及到的地方有很多,只是大家可能不知道这个东西,就是event loop机制导致的这种渲染方式,举个:chestnut::

console.log(1)
setTimeout(()=>{
    console.log(3)
},1)
console.log(2)
复制代码

显而易见,这个结果就是:1、2、3

不管setTimeout放在当前作用域的什么位置,结果都是1、2、3,为什么,这就是event loop事件循环机制。

大家都知道,js是单线程的,如果对多进程和多线程不是特别了解,可以参考这两篇文章:

浏览器多进程架构

浏览器渲染进程多线程

通过这两篇文章你了解什么是多进程多线程以后,可能看起来这里会好很多。

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

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

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

setTimeout是延迟执行,但是不是延迟的时间后立即执行的

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

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

主线程

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

//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)
}
复制代码

结果是1、4、6、3、12、8、2、11、10、9、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、4、6、3、12、8、2、11、10、9、7、14、16、15、13

因为这一块不涉及到事件循环里面,但是属于一个比较例外的方法,这里只做简单讲解,不做深入研究。

总结

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

1、先执行主线程
2、遇到宏队列(macrotask)放到宏队列(macrotask)
3、遇到微队列(microtask)放到微队列(microtask)
4、主线程执行完毕
5、执行微队列(microtask),微队列(microtask)执行完毕
6、执行一次宏队列(macrotask)中的一个任务,执行完毕
7、执行微队列(microtask),执行完毕
8、依次循环。。。

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

结束语

这些,是我在网上看一些大神的讲解,和自己对js事件循环机制(event loop)的理解,写的一篇总结性的文章,如果当中有哪些写的不对的地方,还请大家指出,我会在最短时间内作出调整。如果觉得谢的还行,帮忙给个赞吧。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PCI Express 体系结构导读

PCI Express 体系结构导读

王齐 / 机械工业 / 2010-3 / 55.00元

《PCI Express 体系结构导读》讲述了与PCI及PCI Express总线相关的最为基础的内容,并介绍了一些必要的、与PCI总线相关的处理器体系结构知识,这也是《PCI Express 体系结构导读》的重点所在。深入理解处理器体系结构是理解PCI与PCI Express总线的重要基础。 读者通过对《PCI Express 体系结构导读》的学习,可超越PCI与PCI Express总线......一起来看看 《PCI Express 体系结构导读》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具