JavaScript执行:Promise 为什么比setTimeout先执行
ES3和更早版本,JavaScript引擎在接收到宿主环境传递的一段代码,就直接运行了。
ES6之后,JavaScript引入Promise,这样,不需要浏览器的安排,JavaScript引擎本身也可以发起任务。
微任务和宏任务
JavaScript等待宿主环境分配宏观任务,在操作系统中,通常等待的行为都是一个事件循环,所以在Node术语中,把这部分称为事件循环。
在底层C/C++代码中,这个事件循环是一个跑在独立线程中的循环,我们用伪代码来表示,大概是这样的:
while (true) {
r = wait();
execute(r)
}
整个循环做的事情基本就是反复”等待-循环”,实际还有判断循环是否结束、宏观任务队列等逻辑。
这里可以理解为:宏观任务的队列就相当于事件循环。
在宏观任务中,JavaScript的Promise还会产生异步代码,JavaScript必须保证这些异步代码在一个宏观任务中完成。因此,一个宏观任务又包含一个微观任务队列。
Promise永远在队列添加微观任务,setTimeout等宿主API添加宏观任务。
Promise
Promise是JavaScript提供的一种标准化的异步管理方式。总体思想是需要进行IO、等待或其他异步操作的函数,不返回真实的结果,而是返回一个”承诺”,函数的调用方可以在合适的时机,选择等待这个承诺实现(通过Promise.then方法的回调)。
Promise的回调是一个异步执行过程。
var r = new Promise(function(resolve, reject){
console.log("a");
resolve()
});
setTimeout(()=>console.log("d"), 0)
r.then(() => console.log("c"));
console.log("b")
不论代码顺序如何,d必定在c之后,因为Promise产生的是JavaScript引擎内部的微任务,而setTimeout是浏览器API,它产生的是宏任务。
总结一下异步执行的顺序:
首先分析有多少个宏任务;
每个宏任务中,分析有多少个微任务;
根据调用次序,确定宏任务中的微任务执行顺序;
根据宏任务的触发规则和调用次序,确定宏任务的执行顺序;
确定整个顺序。
async/await
ES6引入新特性,改进跟Promise配合,它提供了用for、if等代码结构来编写异步的方式。它的运行时基础是Promise。
async函数必定返回Promise,我们把返回Promise的函数都可以认定为是异步函数。
async函数强大之处在于,它是可以嵌套的。我们在定义一批原子操作的情况下,可以利用async函数组合出新的async函数。
此外,generator/iterator也常常被跟异步一起来讲,我们必须说明 generator/iterator 并非异步代码,只是在缺少async/await的时候,一些框架(最著名的要数co)使用这样的特性模拟async/await。
此文章为7月Day6学习笔记,内容来源于极客时间《重学前端》,日拱一卒,每天进步一点点💪💪
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net