JavaScript运行机制

156 阅读2分钟

单线程

众所周知,JavaScript的设计思想是单线程。如果所有任务都放入任务队列中,依次来执行的话,后果很严重,因为有的任务需要等待,像Ajax请求,在这期间就必定会造成CPU空闲,浪费了大量资源,造成阻塞。用户的体验可想而知。虽然HTML5添加web Worker使得JS拥有创建多线程的能力,但子线程并不能对DOM元素进行操作,单线程的本质并没有改变。

那单线程如何实现无阻塞运行呢?

就是把需要等待的任务先放一边,优先执行当前可以执行的任务,这是人之常情,我们人类很容易理解,就像烧饭做菜,你是先淘好米后就直接炒菜呢?还是一定要先等饭熟了再炒菜呢?答案不言自明。

任务队列

同步任务/异步任务

我们上文说到需要等待的任务放一边,那放哪里呢?我们称之为任务队列。这样从最高的维度我们就对任务做了区分,不需要等待执行的任务叫同步任务,它会优先进入JavaScript主线程,形成一个执行栈(Exection Context Stack),依次执行;而需要你等待的就是异步任务,它会放入事件表(Event Table)并注册函数,当任务完成后则回到任务队列(Event Queue)。

当执行栈为空时,就读取任务队列的任务来执行,主线程不断的检查任务队列,不为空就一直循环往复的执行,这就是事件循环(Event Loop)。

image.png

异步任务一般包括:Ajax请求、Dom事件操作、定时器、延时器等花费时间较长操作。

宏任务/微任务

不同的异步任务如果都放在同一个任务队列中,会按照顺序执行吗?答案是不会。实际上异步任务还有划分为宏任务和微任务,它们会进入不同的队列。

宏任务(macro-task)主要有:script代码块、延时器(setTimeout)、定时器(setInterval)等
微任务(micro-task)主要是:ajax请求、process.nextTick等

宏任务一般是依赖于宿主环境,而微观任务是语言引擎来发起。