事件循环(event loop)
- JS分为同步任务和异步任务,同步任务会在主线程上执行(形成执行栈,先进后出),异步任务会先放置在任务队列中(先进先出);
- 当主线程上的同步任务全部执行完成后,js会在任务队列中依次取出异步任务并执行。
- JS主线程不断的循环往复的从任务队列中读取任务,执行任务,这中运行机制遍称为事件循环。
但事件循环中的任务队列并不是唯一的,每个事件循环都有一个微任务队列以及多个宏任务队列。
宏任务(Macrotasks)与微任务(Microtasks)
异步任务可分为 宏任务 和 微任务两类,不同的API注册的异步任务会依次进入自身对应的队列中,然后等待 Event Loop 将它们依次压入执行栈中执行。从本质上来说,宏任务是ES6语法规定的,微任务是浏览器规定的,微任务的优先级要高于宏任务。
宏任务包含的API有:script(整体代码)、setTimeout、setInterval等
微任务包含的API有:Promise等
因此一次事件循环的大致过程为:
------------------------------------------
Promise 与Async/Await
Async/Await可看作是Promise异步执行的优化,在其之前,Promise需要多个then()来链式调用,而有了await,就不必这么繁琐。
从定义上来看, async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。
Promise中的异步体现在then和catch中,所以写在Promise中的代码是被当做同步任务立即执行的。而在async/await中,在出现await出现之前,其中的代码也是立即执行的。那么出现了await时候发生了什么呢?
其实,Async函数会返回一个Promise对象,Await用于等待函数的返回值,这个返回值可以是一个Promise对象,也可以是任意一个表达式的结果。
- 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
- 如果它等到的是一个 Promise 对象,await 会阻塞当前路径后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
那么Promise中的代码会如何执行呢?
其实,Promise 构造函数中的第一个参数,是在 new 的时候执行,构造函数执行时,里面的参数进入执行栈执行;而后续的 .then 则会被分发到 microtask 的 Promise 队列中去。同理,在async/await中,函数从上到下依次执行,当碰到返回值时,await
会等待函数返回值并且将其放置在对应的任务队列后让出执行栈,JS向下执行其余代码,之后await得到返回值,执行await语句。
练习题
学了这么多,接下来我们用几道题巩固一下知识。
练习一
function testSometing() {
console.log("执行testSometing");
return "testSometing";
}
async function testAsync() {
console.log("执行testAsync");
return Promise.resolve("hello async");
}
async function test() {
console.log("test start...");
const v1 = await testSometing();//关键点1
console.log(v1);
const v2 = await testAsync();
console.log(v2);
console.log(v1, v2);
}
test();
var promise = new Promise((resolve)=> {
console.log("promise start..");
resolve("promise");});//关键点2
promise.then((val)=> console.log(val))