JS引擎的执行机制

一、前置知识——同步与异步的概念、区分及执行机制

JS最初是被设计在浏览器中使用的,为了避免多个进程同时对一个Dom操作导致冲突,因此将它设计成了单线程语言。

但是JS是能够实现异步操作的,假设JS中不存在异步,代码都自上而下顺序执行,那么如果上一行代码解析时间很长的话,下面的代码就会被阻塞不执行,严重影响用户体验,而实现异步操作主要通过事件循环(event loop)操作。看以下例子:

console.log(1)

setTimeout(function(){

       console.log(2)

 },0)

console.log(3)          //最终打印顺序为1 3 2

如果不了解JS同步和异步任务执行的机制的话,会在这里出现一个疑惑:虽然console.log(2)放在一个延时执行语句里,但延时执行的时间为0,究竟是2先打印还是3先打印?

为了分析上述代码是如何执行的,我们首先必须要了解同步与异步的概念,并能够区分它们。

同步代码不管之前代码输出的结果是什么,只要程序运行到这个语句了都会执行,如上面的console.log(2)和console.log(3),同步代码并没有执行的先决条件。而异步则不同,它需要满足一定的条件后才执行代码,可以是等待一段时间、等待一个事件发生等等,如上面的setTimeout,即使等待时间为0,它也属于异步操作。

同步与异步操作可以按以下规则进行划分:

1、定时器都是异步操作

2、事件绑定都是异步操作

3、AJAX中一般我们都采取异步操作(也可以同步)

4、回调函数可以理解为异步(不是严谨的异步操作)

剩下的都是同步处理

其次,要知道JS执行代码的机制

1、首先判断代码是同步还是异步,同步则进入主线程,异步就进入event table(事件表)

2、异步任务在event table中注册函数,当满足执行条件后,被推入event queue(事件队列)

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

利用上述的知识来分析一下前面的代码

console.log(1)                  //console.log(1)是同步任务,放入主线程中

setTimeout(function(){      //setTimeout()是异步任务放入event table中,0s后将里面的函数推入event queue

       console.log(2)       

},0)

console.log(3)                   //console.log(3)是同步任务,放入主线程中

当主线程运行完打印1、3的任务后,会去event queue查看是否有可执行的语句,从而执行setTimeout里面的函数


二、宏任务与微任务

当了解完上面的知识点之后觉得自己已经掌握JS运行代码的机制,然后当看到下面这个代码的运行结果时一脸懵逼

按照前面的思路进行分析执行顺序应该是这样

1、setTimeout()是异步任务,放入event table,0秒后里面的函数被推入event queue

2、new promise是同步任务,直接进入主线程执行console.log("马上执行for循环啦")及for循环

3、.then是异步任务,放入event table,promise的函数执行完后.then里面的函数被推入event queue

4、console.log("代码执行结束")是同步任务,直接进入主线程执行

5、主线程任务运行完毕后,查看event queue,里面有2个可执行的任务,按任务被推入队列的顺序执行代码,先执行setTimeout()再执行.then

按照这个思路打印结果应该为:马上执行for循环啦--代码执行结束--定时器开始啦--执行then函数

但实际上这段代码的运行结果为:马上执行for循环啦--代码执行结束--执行then函数--定时器开始啦

。。。。。

首先,前面关于同步和异步代码执行的机制并没有错,但在JS执行机制中,还有另一种机制的划分:宏任务(macro-task)和微任务(micro-task)

1、宏任务:包括整体代码script、setTimeout、setInterval

2、微任务:包括promise、process.nextTick

宏任务与微任务的执行机制如下:

1、首先执行宏任务,过程中如果遇到微任务,将其放入微任务的event queue

2、当前宏任务执行完后,查看微任务的event queue,依次执行里面的任务

3、该轮微任务执行完毕后,下一轮event loop查看宏任务的event queue是否有任务并执行

 

结合同步、异步、宏任务、微任务的机制来分析上面给出的代码

1、先执行script里的宏任务,遇到setTimeout()异步任务,0秒后将其放入宏任务的event queue中

2、遇到new promise同步任务,立即执行,打印"马上执行for循环啦"

3、遇到.then异步任务,将其放入微任务的event queue中

4、遇到console.log()同步任务,立即执行,打印"代码执行结束"

5、至此,本轮script下的宏任务执行完毕,查看本轮的微任务,发现有一个.then任务,执行里面的函数,打印"执行then函数啦"

6、到这里,本轮的宏任务及微任务都执行完了,本轮event loop结束

7、下一轮event loop开始,在宏任务的event queue里发现有一个setTimeout()任务,执行里面的函数,打印"定时器开始啦"

所以最后结果为:马上执行for循环啦--代码执行结束--执行then函数啦--定时器开始啦


三、关于setTimeout

看了上面的例子,就会发现,setTimeout实际上并不是按照我们给定的延时时间来执行的,例如 

setTimeout(function(){

       console.log("执行延时函数啦")

},5000)

以上代码并不意味着5s后执行setTimeout里面的函数,而是代表5s后setTimeout里的函数会被推入event queue中,当主线程空闲时,才会去执行这段代码,如果主线程任务执行了10s,那这个函数就只能10s后再执行了

所以执行setTimeout里面的代码需满足2个条件:(1)设定时间到达;(2)主线程空闲;当2个条件都满足代码才会被执行



 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值