JavaScript运行机制

作者: LeoZzz | 来源:发表于2019-04-12 11:43 被阅读12次

    一、前言

    好久没有更新简书了,因为工作忙的原因(主要是懒 - -)。今天给大家分享一篇js运行机制的东西,希望对读者有帮助。

    二、js单线程

    setTimeout(() => { console.log('runing') }, 3000);
    通常我们会这样理解:这段代码表示1秒后,会执行setTimeout里面那个函数。
    然而,这种解释并不准确, 那应该怎么理解呢? 我们先来理解JavaScript的远行机制。
    本文最后我会给出JavaScript的运行机制完整图。但在这之前,我想先来给大家解释几个问题。

    1. JavaScript为什么是单线程的?

    JavaScript设计的初衷是用在浏览器中, 那么,我们来想象一下,如果JavaScript是多线程的话。

    必然可以有两个进程,process1和process2,那么这两个进程可以同时对同一个DOM进行操作。如果这个时候,一个进程要删除这个DOM,另一个进程要编辑这个DOM。启不是矛盾嘛。

    所以,这样应该更好理解,JS为什么是单线程了。

    2. JavaScript为什么需要异步?

    单线程为什么需要异步呢?

    JavaScript如果不存在异步,而是自上而下执行,这样的话,假如上一行解析时间很长,那么下面的代码直接就会被阻塞。这种现象对于用户来说,意味着"卡死"。严重影响用户流失,这样解释好理解吧,所以JavaScript需要异步处理。

    3. 单线程如何实现异步呢?

    JavaScript竟然需要异步,那么它是如何实现异步的呢?

    JavaScript是通过事件循环(event loop)来实现的,事件循环机制也就是今天要说的JavaScript运行机制。

    (一)同步任务和异步任务

    来看一段代码:
    console.log('Number 1');
    setTimeout(() => { console.log('Number 2'); }, 3000);
    console.log('Number 3');
    首先这段代码输出结果是啥?

    输出:1 3 2

    其中setTimeout需要延迟一段时间才去执行,这类代码就是异步代码。

    看到这个结果,所以通常我们都这么理解JS的执行原理:

    第一,判断JS是同步还是异步,同步进入主线程,异步则进入event table。

    第二,异步任务在event table中注册函数,当满足触发条件后,被推入event queue(事件队列)。

    第三,同步任务进入主线程后一直执行,直到主线程空闲,才会去event queue中查看是否有可执行的异步任务,如果有就推入主线程。

    按到这个逻辑,上面这段实例代码,是不是就很好理解了。1,3是同步任务进入主要线程,自上而下执行,2是异步任务,满足触发条件后,推入事件队列,等待主线程有空时调用。

    (二)宏任务(macro-task)和微任务(micro-task)

    然而,按照同步和异步任务来理解JS的运行机制似乎并不准确。

    来看一段代码。看看它的输出顺序。

    案例一

    上面这段代码,按同步和异步的理解,输出结果是:2,4,1,3。因为2,4是同步任务,按顺序在主线程自上而下执行,而1,3是异步任务,按顺序在主线程有空后自先而后执行。

    可事实输出并不是这个结果,而是这样的:2,4,3,1。为什么呢?来理解一下宏任务和微任务。

    宠任务:包括整体script代码,setTimeout,setInterval。

    微任务:Promise,process.nextTick。

    来看原理图:

    JavaScript事件循环

    嗯,对,这就是JS的运行机制。也就是事件循环。解释一下:

    第一,执行一个宏任务(主线程的同步script代码),过程中如果遇到微任务,就将其放到微任务的事件队列里。

    第二,当前宏任务执行完成后,会查微任务的事件队列,将将全部的微任务依次执行完,再去依次执行宏任务事件队列。

    上面代码中promise的then是一微任务,因此它的执行在setTimeout之前。

    需要注意的是:在node环境下,process.nextTick的优先级高于promise。也就是可以简单理解为,在宏任务结束后会先执行微任务队列中的nextTickQueue部分,然后才会执行微任务中的promise部分。

    三、总结

    js在执行事件时,遇到一个宏任务(script(整体代码、setTimeout、setInterval、UI 渲染、 I/O、postMessage、 MessageChannel、setImmediate(Node.js 环境) ),先去执行;检测是否存在微任务,如果有,先放入事件队列,等到宏任务执行完毕后,再去执行,直到所有微任务执行完毕,然后再依次去执行宏任务,就这样不断的宏任务-->微任务-->宏任务的循环。

    相关文章

      网友评论

        本文标题:JavaScript运行机制

        本文链接:https://www.haomeiwen.com/subject/iagqwqtx.html