ECMAScript - async-await 初步
此篇是上篇Promise的续
引例:顺序读取并输出3个文件
// 读取函数
const fs = require('fs');
function readFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if(err) {
return reject(err);
}
resolve(data.toString());
})
})
}
// async await
async function readFileFor3() {
console.log(await readFile("./1"));
console.log(await readFile("./2"));
console.log(await readFile("./3"));
}
特点:
1. await语句只能放在async函数中
否则会出错。
2. await后面紧跟着一个Promise对象,只有当这个Promise对象的状态变成fulfiled(以下fulfiled等同于resolved)时,才会执行后面的代码
文中以下Promise对象执行“完成”,均表示Promise状态变成了fulfiled,变成rejected的情况在后面讨论。
3. await后面的Promise对象将返回它resolve()的值;
因此可以直接用变量赋值接收。
4. async函数执行,返回一个Promise对象,仅当函数中所有的await后面的Promise都执行完,才会改变状态,此时可用then()进行回调处理;
即所有的Promise都fulfiled。
5. await后面的Promise对象如果变更为rejected状态,则整个async函数都会终止执行,除非对该错误进行处理
如果不进行错误处理,则整个async函数会终止执行,async函数返回的Promise也不可以用then方法处理,因此错误处理显得很重要,方法也比较多,只简单记录
对单个await进行错误处理
await后面紧跟着的是一个Promise对象,因此对其进行.catch()即可,这种方法不影响后面代码的执行
async function readFileFor3() {
console.log(await readFile("./1").catch((err) => {console.log(err)}));
console.log(await readFile("./21").catch((err) => {console.log(err)}));
console.log(await readFile("./3").catch((err) => {console.log(err)}));
}
对async函数返回的Promise进行错误处理
readFile("./3").catch((err) => {console.log(err)});
即使这样可以捕捉到错误,依然要记得,此时产生错误的await后面的代码并没有执行。
继发和并发
每一个await是独立的异步操作,只有当上一个await完成,才会执行下一个await,例如
async function waitForAll() {
await waitTime(3000); // 每个waitTime(3000)会花费3s的时间
await waitTime(3000);
await waitTime(3000);
// 等待 9s
console.log("all over");
}
如果需要多个await同时开始执行,可以利用 Promise.all([promise1, promise2...]),比如
async function waitForAll() {
await Promise.all([waitTime(3000), waitTime(3000), waitTime(3000)]);
// 3s 即可完成
console.log("all over");
}
Promise.all()
Promise.all()方法将多个 Promise 实例,包装成一个新的 Promise 实例。
接收一个数组作为参数,数组元素均为Promise实例,返回新的Promise实例,设为p
- 只有所有元素的状态都变成fulfilled,p的状态才会变成fulfilled,此时元素的返回值组成一个数组,传递给p的回调函数;
- 只要其中有一个被rejected,p的状态就变成rejected,此时第一个rejected的实例的返回值,会传递给p的回调函数
应用:封装定时器的增强函数
function TimerWrapper(fn) {
return async () => {
let time = 0;
const timeout = setInterval(() => {
console.log(++time)
}, 1000);
// await 执行函数
await fn();
// 最后清除定时器
clearInterval(timeout);
}
}
这个函数可以传入一个函数,返回的函数执行时会进行计时,从而很好的说明执行顺序和时间的问题。
async function readFileRaw() {
await Promise.all([waitTime(3000), waitTime(3000), waitTime(3000)]);
console.log("all over");
}
TimerWrapper(readFileRaw1)()
/*
* 输出: 1,2,allover
*
*/
附录: 晚秋