vlambda博客
学习文章列表

终于明白的JS——消息队列与事件循环











我们知道JavaScript是一种单线程、异步、非阻塞、解释型脚本语言,那么JS代码是如何在浏览器上运行起来的?是如何处理各类消息和任务的呢?


概念解释



#

单线程

在执行程序时,所走的程序路径必须按顺序执行,前面的执行完之后才能执行后面的。

#

异步

同步就是能够立即拿到调用结果,异步就是可能得等待一段时间才能拿到调用结果。

#

非阻塞

在这样的单线程上执行异步请求时不会造成后续任务阻塞。

#

解释型语言

计算机语言分两种:编译型语言和解释型语言

编译型语言在程序执行之前,需要经过编译器的编译过程,形成机器能读懂的二进制文件。例如:c、c++

解释型语言是在程序运行时才进行编译,编译前需要确定变量的类型,效率较低,但是对不同系统平台有较大的兼容性。例如:Python、JavaScript。


产生原因


JS只有一个线程(由JS引擎维护),这个线程负责解释和执行JS代码,称其为主线程,在这个主线程上,所有的代码按照顺序执行。

但是网页中常常会出现发起AJAX请求或者用户交互的功能。如果一个请求的响应时间是500ms,那么以这种单线程的方式的话,页面需要等待500ms之后才会处理后面的任务,用户的交互也不能及时反馈。这种一个一个同步执行的操作容易使页面呈卡死状态。因此大部分耗时的任务在JS中都会通过异步的方式实现。

虽然JS引擎只维护一个主线程用来解释执行JS代码,但是浏览器上是存在多个线程的,他们可以用来处理AJAX请求,DOM渲染,定时器等,同时,浏览器还维护着一个消息队列,当JS主线程遇到异步代码时,就会先发送给这个消息队列,等到主线程的任务执行完之后再执行消息队列中的任务。而当消息队列中的任务处理完了,主线程又有新的任务时,又会重新执行主线程中的新任务,这就是任务循环。

总的来说,JS是个单线程,然而主线程的任务又非常的繁忙,既要处理DOM,又要计算样式,处理布局,同时还要处理Javascript任务以及各种输入事件。对于这么多任务需要有条不紊的执行,那么就需要有一个机制来统筹调度,也就产生了消息队列和任务循环。


消息队列


消息队列其实是一种数据结构,存放主线程中遇到的异步任务,符合“先进先出”的特点,即添加任务往尾部添加,取出任务从头部取。

值得注意的是whatwg(网页超文本技术工作小组)提出的几个规范:

#

An event loop has one or more task queues.

一个事件循环有一个或多个任务队列。

#

Task queues are sets not queues, because step one of the event loop processing model grabs the first runnable task from the chosen queue, instead of dequeuing the first task.

任务队列是sets而不是queues,因为事件循环处理模型的第一步是从选定的队列中获取一个可运行的任务,而不是将第一个任务出队。也就是说消息队列不算真正的队列,因为有很多任务队列,不同的任务来源会被推入到不同的任务队列中。

#

every task source must be associated with a specific task queue

每一种任务来源都必须关联一个任务队列。(但其实一个任务队列里可以有多个不同来源的任务)任务来源比如DOM操作、UI事件、请求处理等。上一轮事件循环结束之后,会选择一个高优先级的任务队列执行。


事件循环


在线程运行过程中,接受并执行新的任务,就需要采用事件循环机制。

终于明白的JS——消息队列与事件循环

图片源自一只菜鸟攻城狮啊

绘图者根据whatwg中阐述的执行流程进行绘制,可以很清晰的看出,事件循环机制也就是:

1. 先执行主线程中的同步任务;

2. 执行完成之后,会判断延迟队列中是否有到期任务;

3. 若有则取出延迟队列中的到期任务进行执行,若没有则取出消息队列中优先级最高的可执行任务进行执行;

4. 然后完成下一次任务循环。


宏任务与微任务


消息队列中的任务称为宏任务。

根据上面对消息队列和事件循环的描述可以清晰的知道浏览器执行JS的机制。但是消息队列中这种粗时间颗粒度的任务已经不能胜任部分领域的需求,于是出现了微任务。微任务可以在实时性和效率之间做一个很有效的权衡,所以消息队列中的任务称为宏任务,而每个宏任务都包含了一个微任务队列。

下面对宏任务类型和微任务类型做一个整理(有些任务在浏览器环境和node环境下有不同的表现):

宏任务


_

浏览器

Node

I/O

setTimeout

setInterval

requestAnimationFrame

setImmediate


微任务


_

浏览器

Node

Promise.then catch finally

MutationObserver

process.nextTick


微任务宏任务执行图

终于明白的JS——消息队列与事件循环

图片源自:

https://www.cnblogs.com/amiezhang/p/11349450.html


最后一个读后小练习:下面的代码执行顺序是怎么样的呢?

ps:这也是笔者在面试过程中遇到的真实问题。

new promise((resove, reject) => {    console.log(1)}).then(()=>{    console.log(2)})console.log(3)setTimeout(()=>{    console.log(4)}, 0)

回复“消息队列”获取答案和解析吧。


参考链接:

https://segmentfault.com/a/1190000013039660

https://www.yuque.com/suihangadam/powcpt/mz5a92

https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model




六芒星空间站

产品思维 / 设计洞察 / 技术干货

每周一、三、五更新

终于明白的JS——消息队列与事件循环
六芒星空间站

官网|yuhao.tech

终于明白的JS——消息队列与事件循环

关于我们

终于明白的JS——消息队列与事件循环

       六芒星空间站里有一群充满朝气、睿智笃定又目标明确的年轻人,并在人机交互、用户体验、技术研发、机器学习等方向进行深入研究。

        我们不断探索在新技术驱动下的创新产品方案,如语音交互、机器学习、知识图谱等,力争在科技大浪中提升能力,立于浪潮之上。我们希望发挥我们在互联网行业的能力特长,利用我们的能力与所积累的行业经验,解决互联网相关的问题,为好的想法提供落地的解决方案支持。


昱皓科技

yuhao.tech


戳下面的
原文阅读
访问我们的官网