学习Event Loop时,有async/await,倒是学习过,也就看过就忘了,在这深入的学习下,做个总结。
虽然是ES7里面的,但是很早就出来被使用,面试的时候也被问到过。接触异步最多的还是接口、setTimeout、Promise,相对于async,可能对promise更熟悉些,其实async也算得上Generator 函数的语法糖。promise调用then方法链式回调,async代码更优雅,符合阅读习惯,使用await使用”同步“的方式实现了异步。
当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
1)async声明函数里有异步操作,await表示等待(只能在async函数内部使用),相当于promise里的then
如果B接口的参数需要A接口请求结果,需要等待A接口请求完成,才能请求B接口,如下实例:
async function ay(){
// fun1完成以后fun2才会发生 继发关系
let t1 = await fun1()
let t2 = await fun2(t1)
console.log(t2)
}
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1)
},1000)
})
}
function fun2(p) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 + p)
},2000)
})
}
ay()// 3s之后输出3
2)返回值是promise对象,使用then获取asycn返回的结果。
async function fun3(x) {
return x
}
let result = fun3(3)
console.log(result)
async function fun3(x) {
return x
}
fun3(3).then((r) => console.log(r))
3)两个异步同时进行
当需要两个接口同时请求成功之后才进行下一步操作,如何处理。
按如下顺序执行,是需要等待fun1加载完成后,fun2才能开始加载,时长就会变长。
let t1 = await fun1()
let t2 = await fun2()
fun1和fun2是同时触发,缩短程序执行时间
async function ay(){
let [ t1, t2 ] = await Promise.all([ fun1(), fun2() ])
console.log(t1)
console.log(t2)
}
async function fun1() {
return new Promise((resolve, reject) => {
setTimeout(function(){
return resolve(1)
},2000)
})
}
async function fun2(p) {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(2)
},2000)
})
}
ay()
4)捕获异常
在实际场景中,存在接口获取数据异常,如果返回reject,执行到await会报错,并且不会执行后续的代码,由于async返回的是promise对象,可以使用catch去捕获,也可以使用try/catch
async function ay(){
let t1 = await fun1()
.then(result => console.log(result))
.catch((err) => console.log(err))
}
function fun1() {
return new Promise((resolve, reject) => {
setTimeout(function(){
reject('1')
},2000)
})
}
5)实现原理
将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
// ...
});
}
6)Event Loop
在浏览器中,异步机制是借助 event loop 来实现的,event loop 是异步的一种实现机制。
可以看下异步任务执行顺序
console.log('script start') // 1️⃣
async function async1() { // 1️⃣
await async2() // 1️⃣
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => { // 1️⃣
console.log('Promise') // 1️⃣
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end') // 1️⃣
上图标记的1️⃣都是属于同步任务的,可能await async2()会有些疑问,转换成promise:
function async1() {
return new Promise((resolve, reject)=>{
async2()
resolve()
}).then(() => {
console.log('async1 end')
})
}
function async2() {
console.log('async2 end')
}
async1()
看到一个promise的实例
new Promise(resolve => {
resolve(1);
Promise.resolve().then(() => console.log(2));
console.log(4)
}).then(t => console.log(t)); // a
console.log(3);
是阮老师推特上的一道题,首先 Promise 构造函数中的对象同步执行,碰到 resolve(1),将当前 Promise 标记为 resolve,但是注意它 then 的回调函数还没有被注册,因为还没有执行到 a 处。继续执行又碰到一个 Promise,然后也立刻被 resolved 了,并且执行它的 then 注册,将第二个 then 的回调函数推入空的 microtaskQueue 中。继续执行输出一个 4,然后 a 处的 then 现在才开始注册,将第一个 Promise 的 then 回调函数推入 microtaskQueue 中。继续执行输出一个 3。现在 task queue 中的任务已经执行完毕,到了 microtask checkpoint flag,发现有两个 microtask,按照添加的顺序执行,第一个输出一个 2,第二个输出一个 1,最后再更新一下 UI 然后这一轮 event loop 就结束了,最终的输出是"4 3 2 1"
摘自:fi3ework