tags: [词法,转载]
categories: 重学前端系列笔记
cover: “https://cdn.nlark.com/yuque/0/2019/jpeg/221851/1555489235300-77ec8cfd-2be0-448e-a3d3-9dead9ff2d33.jpeg“
前言
当我们接到一段 JavaScript
代码的时候,传递给 JavaScript
引擎要求其执行。在遇到事件的时候,会继续将其中的一些事件代码交给 JavaScript
引擎执行,当然也会提供一些 API
给 JavaScript
引擎,这样的 API
允许引擎在特定时机执行某段代码。
我们可以理解为 JavaScript
引擎存在于内存中,它一直在运转,在等待着我们将 JavaScript
片段代码或者事件交给它进行执行处理,但是页面一般同时会有很多代码需要执行,那么就需要指定一个顺序。
宏任务与微任务
我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务。
JavaScript
引擎一直在等待着宿主分配宏观任务,这个等待分配,然后执行代码的过程,称之为 evenLoop
// 伪代码表述简单的evenLoop
while(true) {
r = wait(); // 等待宿主分配任务
execute(r); // 分配任务之后,执行该任务
}
当然在等待以及执行的时候,还有其他的过程,像是检查任务队列,判断任务是否达到执行时机,判断同步的上一个任务是不是执行完毕等等。
而宏任务的队列就是事件循环。而 JavaScript
在执行宏观任务的时候,如果这个任务是异步的, JavaScript
要保证异步代码在一个宏观任务中执行,那么宏观任务中就有微任务队列
有了宏任务以及微任务概念之后,就可以理解,引擎产生的任务会在任务尾部添加微任务。而宿主产生的任务会直接扔在宏任务内
promise
var promise = new Promise(function(resolve, reject){
console.log("a");
resolve();
})
promise.then(function(){
console.log("b");
});
console.log("c");
// 打印的顺序是 acb;
上面的运行结果之所是 acb
是因为 new
一个构造函数,会执行构造函数内的函数,然后虽然 console.log("c")
在 .then()
后面,但是因为 resolve()
是异步的。所以会先打印 c
setTimeout与promise混合使用
var promise = new Promise(function(resolve, reject){
console.log("a");
resolve()
});
setTimeout(()=>console.log("d"), 0)
promise.then(() => console.log("c"));
console.log("b")
上面的运行结果是 abcd
,代码从上向下扫射运行。先是运行 new
关键字的构造函数里的代码,打印 a
,遇到 resolve()
运行 .then
的代码,但是由于这个是异步的,所以就会继续往下运行代码, setTimeout
的代码会放在任务队列里。而事件队列里的代码会等主线程的代码运行完之后,才会检查事件队列。所以下一步打印的是 b
,然后由于 promise
是 JavaScript
引擎产生的微任务,而 setTimeout
是浏览器产生的宏任务,所有先打印 c
再打印 d
微任务是永远优先于宏任务的
setTimeout(()=>console.log("d"), 0)
var promise = new Promise(function(resolve, reject){
resolve()
});
promise.then(() => {
var begin = Date.now();
while(Date.now() - begin < 1000);
console.log("c1")
new Promise(function(resolve, reject){
resolve()
}).then(() => console.log("c2"))
});
由上面的两个示例可以得出
- 先确定有多少个宏任务、
- 然后确定每个宏任务里有多少个微任务
- 根据调用的循序,确定每个微任务的执行顺序
- 然后将整个的环境执行顺序确定下来