当我们在调用setTimeout的时候,实际上在调什么(宏任务本质)

68 阅读4分钟

原子文章: segmentfault.com/a/119000001…

web前端的事件循环机制,是web程序运行的底层逻辑,当我在分析这套机制的时候,总是分不清楚,它的启动对象是哪一个,是js引擎,还是浏览器,这个问题在面试的时候,我也和面试官有过分歧,我认为包括js引擎在内的,eventloop、宏任务(ajax、setTimeout),都是浏览器本身提供的线程处理方案,但是面试官认为,这套机制是js引擎本身的处理,这个问题给我的认知留下了疑惑

昨天又见到事件轮询机制,给了一套经典的promise、setTimeout组合,在思考题目的时候,我就一直在想,setTimeout到底是怎么运行的呢,像setTimeout、ajax这种api在调用的时候,是谁在执行它呢?

首先,像setTimeout、ajax这种,都是调用浏览器提供的api,我们会将他们统称为宏任务,每一个api都(是一个独立的宏任务执行栈)会开启一个独立的浏览器线程,即加入宏任务队列,实际上是开始新的浏览器的线程,也就是相当于,宏任务的本质是一个浏览器的线程,但是微任务不是独立的执行栈,应该是js引擎(不同的环境下的js引擎是不一样的,比如说,chrome浏览器的js引擎是V8引擎,node环境是一个封装了V8引擎的js引擎,safiry和其他浏览器则有自己的js引擎,但是内核相似,但是处理的算法机制不同)内部的一个队列储存结构,而且不是原生的,只是一种ES6的语法糖,微任务队列也不是具体存在的,而是形象的比拟的(实际上,应该是可以创建微任务的方法(比如 Promise)本身是一个注入到全局环境(window对象)中的一个方法,这个方法提供了一个“注册微任务队列并延时执行”的方案,本质上也是手写的)

“当 JavaScript 执行一段脚本的时候,V8会为其创建一个全局执行上下文,同时 V8引擎也会在内部创建一个微任务队列。这个微任务队列就是用来存放微任务的,因为在当前宏任务执行的过程中,有时候会产生多个微任务,这时候就需要使用这个微任务队列来保存这些微任务了。不过这个微任务队列是给 V8引擎内部使用的,所以你是无法通过 JavaScript直接访问的。(原文链接:blog.csdn.net/qq_42198495…

在执行微任务过程中产生的新的微任务并不会推迟到下一个循环中执行,而是在当前的循环中继续执行

微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列”(?)

微任务的产生时机: 那么微任务是怎么产生的呢?在现代浏览器里面,产生微任务有两种方式。

1、使用 MutationObserver监控某个 DOM节点,或者为这个节点添加、删除部分子节点,当 DOM节点发生变化时,就会产生 DOM变化记录的微任务。

2、使用 Promise,当调用 Promise.resolve()或者 Promise.reject()的时候,也会产生微任务(这句话的意思是,当执行到 Promise.resolve()时,才会执行.then(),而then方法才会将其中的回调函数放到微任务队列中,这个微任务队列时由谁来管理的呢?因为产生微任务的方法很多种,肯定不是由Promise来管理的,由浏览器的js引擎?js引擎是如何实现的呢?是未暴露的api吗?)

事件循环机制在这篇文章有详细的说明:www.cnblogs.com/caiyy/p/103…

 总结一句话:

 事件循环机制的核心是事件触发线程,由于执行栈产生异步任务(本质是触发其他浏览器非js引擎线程),异步任务完成后事件触发线程将其回调函数传入到任务队列(宏任务队列)中,当执行栈为空,任务队列将队列头的回调函数压入执行栈,从而新的一轮循环开始。这就是称为循环的原因。

WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件,http请求,定时器等异步任务