最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 关于JS的事件循环——Event Loop

    正文概述 掘金(PanChao52326)   2021-08-10   566

    前言

    众所周知,JavaScript是一门单线程的语言,虽然HTML5提出了Web Worker标准,允许JavaScript创建多个线程,但是它的子线程却完全受主线控制。且不能用于操作DOM,所以并没有改变JavaScript是单线程语言的本质

    为什么是单线程?

    JavaScript作为一门浏览器的脚本语言,他是用户和浏览器交互的桥梁,其中自然避免不了操作DOM并且渲染DOM这个根本且重要的事,而既然要操作DOM。如果是多线程的话,如果某一时刻,一个线程给一个DOM元素添加一个属性,另一个线程又要删除那个属性时该怎么办呢?要以哪个为主呢?所以它的用途已经决定了它是单线程的本质。

    任务队列

    单线程,这就异味着任务的执行需要一个个执行,而如何保证执行的顺序是可预期的,这时候就要引出任务队列的概念了,既然叫做队列,自然就遵循First-In-First-Out(FIFO)先进先出的原则,最先进入队列的最先执行。

    但这里会出现一个问题,如果一些非阻塞性的任务执行需要耗时很久,如(Ajax请求等),需要时间等待才能执行结束的话,此刻控制的CPU就会造成不必要的资源浪费,所以在JavaScript中,任务又被分为了同步任务(synchronous)异步任务(asynchronous)两种

    同步任务:会进入JS的主线程,在主线程上执行的任务,只有前一个任务执行完毕才会继续下一个任务

    异步任务:异步任务不进入主线程,而是进入Event Table, 满足条件后进入“任务队列”(Task Queue),异步任务的执行,是在异步任务执行有了响应结果之后,在任务队列中推进一个事件,等待“执行栈”中的所有同步任务执行完毕,便读取“任务队列”,看看里面有哪些事件,这些事件对应的异步任务便结束等待,进入执行栈,开始执行

    总结起来,异步任务的运行机制便可以如下

    1. 所有的同步任务都在主线程上执行,形成一个执行栈
    
    2. 主线程之外,会维护一个任务队列,只要异步任务运行有了结果,就在任务队列中放入一个事件
    
    3. 如果执行栈的同步任务执行完毕,系统会读取任务队列,依次执行里面的任务
    
    4. 主线程重复如上的步骤
    

    事件循环和任务队列

    任务队列其实是一个事件的队列,也可以理解为成消息队列,IO设备完成一项任务,就在任务队列中添加一个事件,表示相关对应的异步任务可以进入执行栈了。主线程在执行完同步任务后,就会读取任务队列,其实就是读取里面的事件来执行。

    而任务队列中的事件,除了一些IO设备的事件之外,还包括一些用户行为产生的事件,如点击,双击,获取焦点等回调,这些回调函数(事件)都会被推进任务队列,等待主线程读取然后依次执行

    队列的本质是一个先进先出的数据结构,也就是说执行完的事件就会从队头弹出去,在队列中清空掉,而主线程执行栈的执行,就是读取任务队列中的事件,这个过程是循环不断的,直到队列清空为止,当前的执行环境才会结束,所以这个机制又被称为事件循环(Event Loop)

    例子

    下面,我们举个小例子来说明一下

    console.log(1)
    
    console.log(2)
    
    console.log(3)
    
    setTimeout(function() {
      console.log(0)
    }, 10)
    
    // 输出 1, 2, 3, 0
    

    上面的例子,定时器setTimeout就是一个异步任务,如果剩余的三个console都是同步任务,所以他们会按照JavaScript的运行机制——解释执行,依次进入主线程的执行栈,逐步执行,而setTimeout则会主线程挂起,直到10毫秒后,异步任务结束,便将对应的回调函数(事件)放入事件队列(任务队列)中,由主线程中执行完同步任务后,依次读取。

    上面的例子可以稍微改造一下

    console.log(1)
    
    console.log(2)
    
    console.log(3)
    
    setTimeout(function() {
      console.log(0)
    }, 10)
    
    console.log(5)
    
    // 输出 1, 2, 3,5, 0
    

    这个改造后的例子,我们在定时器后面又加了一个console,但是他输出的顺序依然排在了定时器的前面,这更是说明了,主线程会优先读取完所有的同步任务依次执行,结束后才会读取任务队列依次执行。

    宏任务和微任务

    在我的理解中,JS是分为了同步和异步任务,而异步任务又分为两种,一种是宏任务,一种又是微任务,同步任务的执行顺序优先于异步任务,而微任务又优先于宏任务。

    宏任务

    #浏览器Node
    setTimeoutsetIntervalsetImmediatexrequestAnimationFramex

    微任务

    #浏览器Node
    process.nextTickxMutationObserverxPromise.then catch finallyx

    下面,我们再详细举个例子

    console.log(1)
    
    setTimeout(function(){console.log(2)}, 10)
    
    console.log(4)
    
    function fn() {
      return new Promise((r, j) => {
        console.log(6)
        r()
       
      })
    }
    
    Promise.resolve().then(function() {
      console.log(5)
    })
    
    fn().then(function() {
      console.log(7)
    })
    
    // 输出
    // 1, 4, 6, 5, 7, 2, 
    

    以上的运行机制,便是先遇到到了代码块,姑且也叫同步任务,输出1,然后遇到宏任务,由主线程挂起,进入到Event Table,说的再细一点,是挂起后,进入了宏任务队列(macrotask queue),再之后又遇到代码块console,输出4,继续往下,声明了fn函数,但是还没调用,再往下,遇到了一个promise.then, 将其放入微任务队列(microtask queue), 再继续,我们调用了fn函数,进入了fn的执行环境,输出了6,然后调用了resolve,掉任务放进微任务队列,然后退出fn的执行环境。这时候,主线程的同步任务执行结束,开始扫描微任务队列,第一个输出5, 第二个输出了7,再继续,微任务队列被清空,开始进入宏任务队列扫描,输出了2,至此,这段代码便执行结束了。

    值得注意的是,process.nextTick 永远大于 promise.then,在Node中, _tickCallback在每一次执行完TaskQueue中的一个任务后被调用,而这个_tickCallback中实质上干了两件事:

    1.nextTickQueue中所有任务执行掉(长度最大1e4,Node版本v6.9.1)

    2.第一步执行完后执行_runMicrotasks函数,执行microtask中的部分(promise.then注册的回调)

    所以很明显 process.nextTick > promise.then

    但是,js和nodejs的event loop又存在于一些差异,到时候再找时间写一篇nodejs的event loop相关的文章吧


    下载网 » 关于JS的事件循环——Event Loop

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元