async & await
自动化生成器
上一节中提到了 async / await 是 Promise + generator 的语法糖,那么我们先来看看,这两个东西组合起来能做什么事情
// 模拟网络请求
function requestData(data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(data.toUpperCase());
}, 1000);
});
}
// 生成器
function* getData() {
const res1 = yield requestData("name: ");
const res2 = yield requestData(res1 + "aa");
const res3 = yield requestData(res2 + "bb");
console.log("result: " + res3);
}
// 自动执行的生成器
function createAutoGen(genFn) {
const generator = genFn();
function handler(res) {
const result = generator.next(res);
if (result.done) { // 如果没有迭代结束,那就返回
return result.value;
}
// 多套一层 Promise.resolve 是为了处理 yield 返回非 Promise 对象的情况,可以统一处理
Promise.resolve(result.value).then((res) => {
console.log(res);
handler(res); // 自动执行下一次 next
});
}
handler();
}
createAutoGen(getData);
/* console.log
NAME:
NAME: AA
NAME: AABB
result: NAME: AABB
*/
上面的代码实现了当上一个异步请求结束后,才会自动执行下一段代码的功能
用过 async / await 的同学应该已经看出来了,这个好像就是实现了异步函数。确实是这样,为了后文更好的理解关键字 await 和 返回值 问题,我们可以先了解 async 异步函数的处理场景。
比起生成器,async / await 是使用更加高频的一组关键字: async 用于声明异步函数,await 用于控制暂停与执行的流程
await
在了解了生成器和 Promise 勾结在一起之后会发生什么奇妙的事情之后,那么理解 await 也就水到渠成了。await 可以理解为升级版的 yield
关注以下几点
- 跟
yield不太一样,await会自动控制下一段代码的执行,不需要自己在外部调用形如next的函数(或者封装控制生成器自动执行的函数) - 因为不需要外部调用控制暂停与执行,所以
await获取的值与右侧执行语句的返回值有关 await可以理解为Promise.resolve()的语法糖,上文就是其简易实现。 因此await获取的值也就是右侧执行语句resolve(value)中的value而await后续的代码可以等效为装在Promise.resolve(右侧表达式).then(value => { ...装在这里 })的代码
我们在这里验证一下 await 后面的代码是否是微任务,即上文的实现功能
async function foo() {
console.log("async ----- start");
const res1 = await 1;
console.log(res1);
const res2 = await 2;
console.log(res2);
console.log("async ----- end");
return "end";
}
console.log("script start");
foo();
Promise.resolve("p1").then((res) => console.log(res));
setTimeout(() => {
console.log("setTimeout");
}, 100);
console.log("script end");
/* console.log
script start
async ----- start
script end
1
p1
2
async ----- end
setTimeout
*/
经过验证可以看出来 foo 中第一个 await 之前的代码会同步执行,而后续的代码则是作为微任务执行的
返回值
使用 async function 声明的异步函数返回值是一个 Promise ,如果函数中存在 await 关键字,那么返回的对象就是 <pending> 状态,需要使用另一个 await 接收或者使用 then 处理
使用下面的代码来验证一下
async function foo() {
await "wait";
return "end";
}
// 不做任何处理
async function oriHandler() {
const res = foo();
console.log(res); // Promise { <pending> }
}
// 使用 await
async function awaitHandler() {
const res = await foo();
console.log(res); // end
}
// 使用 then
async function thenHandler() {
const res = foo();
res.then((value) => {
console.log(value); // end
});
}
oriHandler();
awaitHandler();
thenHandler();
小结
如果已经理解了生成器的作用以及其与 Promise 组合使用的实现,在这个基础之上来理解 async / await ,多少会有点收获,同时以后再来看异步函数,可以更加深入的角度来思考问题。
参考资料
《JavaScript高级程序设计》(第四版)
《JavaScript忍者秘籍》(第二版)
后续
...施工中