同系列进阶文章:JavaScript异步编程:async底层原理与promise/generator的关系
语法简介
async/await是一种方便使用promise的特殊语法。
函数前面的async表示:此函数将会返回一个promise,如果函数返回的不是promise,将会包装成一个已经resolved的promise。
async function f() {
return 1;
}
f().then(alert); // 1
// 相当于下面的写法
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
await只在async函数中有效,普通函数中使用await会报语法错误。
let value = await promise;
表示让JavaScript引擎等待直到 promise 完成并返回结果。这个行为不会耗费 CPU 资源,因为引擎可以同时处理其他任务:执行其他脚本,处理事件等。
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done!"), 1000)
});
let result = await promise; // 等待直到 promise resolved后拿到result继续往下执行
alert(result); // "done!"
}
f();
await可以接收Thenable对象(具有 then 方法的对象)并调用then方法,并将resolve,reject 作为参数传入。然后await等到这两个方法中的某个被调用,再处理得到的结果。
class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
setTimeout(() => resolve(this.num * 2), 1000);
}
};
async function f() {
// 等待 1 秒, result 变为 2
let result = await new Thenable(1);
alert(result);
}
f();
如果想定义一个 async 的类方法,在方法前面添加 async 就可以了:
class Waiter {
async wait() {
return await Promise.resolve(1);
}
}
new Waiter().wait().then(alert); // 1
错误处理
如果一个promise被resolved,await promise返回的就是其结果。 如果一个promise被rejected,就会抛出一个错误,就像在那一行有个 throw 语句那样。
async function f() {
await Promise.reject(new Error("Whoops!"));
}
// 相当于:
async function f() {
throw new Error("Whoops!");
}
可以用 try..catch 来捕获上面的错误,就像对一般的 throw 语句那样:
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
如果不使用 try..catch,由f() 产生的 promise 就会被reject,可以在函数调用后添加 .catch 来处理错误。如果没处理错误可以使用全局的unhandledrejection
事件来捕获。
async function f() {
let response = await fetch('http://no-such-url');
}
// f() 变为一个被reject的 promise
f().catch(alert); // TypeError: failed to fetch // (*)
当需要等待多个promise时,可以用Promise.all搭配async/await使用:
async () => {
let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);
}
如果发生错误,也会正常传递:先从失败的 promise 传到 Promise.all,然后变成我们能用 try..catch 处理的异常。
执行顺序
Async/await 是基于 promise 的,所以它内部使用相同的微任务队列,并且相对宏任务来说具有更高的优先级。
async function f() {
return 1;
}
(async () => {
setTimeout(() => alert('timeout'), 0); // 3
await f(); // 1
alert('await'); // 2
})();
在一个普通的函数中,如何调用另一个 async 函数并且拿到返回值?使用then,因为async函数返回一个settled promise。
async function wait() {
await new Promise(resolve => setTimeout(resolve, 1000));
return 10;
}
(function() {
wait().then((value) => alert(value));
})();