JavaScript运行机制

本文详细阐述了JavaScript的单线程特性,解释了任务队列(包括同步和异步任务)、事件循环机制以及如何在Node.js环境中实现这些概念。特别强调了microtask和macrotask的区别,以及它们在事件循环中的作用。

 

一、单线程

JS是单线程,为了不阻塞线程,JS通过事件循环的方案解决耗时任务。

二、任务队列(同步/异步)

1.任务

可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

2.任务队列的类型

任务队列存在两种类型,一种为microtask queue,另一种为macrotask queue。

上图所列出的任务队列均为macrotask queue,而ES6 的 promise[ECMAScript标准]产生的任务队列为microtask queue。

microtask queue:唯一,整个事件循环当中,仅存在一个;执行为同步,同一个事件循环中的microtask会按队列顺序,串行执行完毕;

macrotask queue:不唯一,存在一定的优先级(用户I/O部分优先级更高);异步执行,同一事件循环中,只执行一个。

3.完整的事件循环流程    

将microtask加入到JS运行机制流程中,则:

step1:主线程读取JS代码,此时为同步环境,形成相应的堆和执行栈;

step2:  主线程遇到异步任务,指给对应的异步进程进行处理(WEB API);

step3:  异步进程处理完毕(Ajax返回、DOM事件处罚、Timer到等),将相应的异步任务推入任务队列;

step4:主线程查询任务队列,执行microtask queue,将其按序执行,全部执行完毕;

step5:主线程查询任务队列,执行macrotask queue,取队首任务执行,执行完毕;

step6:重复step4、step5。

microtask queue中的所有callback处在同一个事件循环中,而macrotask queue中的callback有自己的事件循环。

简而言之:同步环境执行 -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个) -> 事件循环1(microtask queue的All)-> 事件循环2(macrotask queue中的一个)...

利用microtask queue可以形成一个同步执行的环境,但如果Microtask queue太长,将导致Macrotask任务长时间执行不了,最终导致用户I/O无响应等,所以使用需慎重

 

三、事件循环(js执行机制)

事件循环是js实现异步的一种方法,也是js的执行机制。

事件循环(Event Loop):

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的第三步。

 

task主要包含:setTimeoutsetIntervalsetImmediateI/OUI交互事件

microtask主要包含:Promiseprocess.nextTickMutaionObserver

 

四、Node.js的Event Loop

(1)V8引擎解析JavaScript脚本。

(2)解析后的代码,调用Node API。

(3)libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。

(4)V8引擎再将结果返回给用户。

Node.js

除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与"任务队列"有关的方法:process.nextTicksetImmediate。它们可以帮助我们加深对"任务队列"的理解。

process.nextTick方法可以在当前"执行栈"的尾部----下一次Event Loop(主线程读取"任务队列")之前----触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前"任务队列"的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像

Node的Event Loop分阶段,阶段有先后,依次是

  • expired timers and intervals,即到期的setTimeout/setInterval
  • I/O events,包含文件,网络等等
  • immediates,通过setImmediate注册的函数
  • close handlers,close事件的回调,比如TCP连接断开

同步任务及每个阶段之后都会清空microtask队列

  • 优先清空next tick queue,即通过process.nextTick注册的函数
  • 再清空other queue,常见的如Promise

而和规范的区别,在于node会清空当前所处阶段的队列,即执行所有task

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

参考文档:http://www.ruanyifeng.com/blog/2014/10/event-loop.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值