摘要:在本教程中,您将学习如何使用 JavaScript async
/ await
关键字编写异步代码。
请注意,要了解 async
/ await
的工作原理,您需要了解 Promise 的工作原理。
JavaScript async / await 关键字简介
在过去,为了处理异步操作,您使用的是 回调函数。但是,嵌套多个回调函数会使您的代码更难维护,从而导致一个臭名昭著的问题,称为回调地狱。
假设您需要按以下顺序执行三个异步操作
- 从数据库中选择一个用户。
- 从 API 获取用户的服务。
- 根据服务器的服务计算服务成本。
以下函数说明了这三个任务。请注意,我们使用 setTimeout()
函数来模拟异步操作。
function getUser(userId, callback) {
console.log('Get user from the database.');
setTimeout(() => {
callback({
userId: userId,
username: 'john'
});
}, 1000);
}
function getServices(user, callback) {
console.log(`Get services of ${user.username} from the API.`);
setTimeout(() => {
callback(['Email', 'VPN', 'CDN']);
}, 2 * 1000);
}
function getServiceCost(services, callback) {
console.log(`Calculate service costs of ${services}.`);
setTimeout(() => {
callback(services.length * 100);
}, 3 * 1000);
}
Code language: JavaScript (javascript)
以下是嵌套回调函数的示例
getUser(100, (user) => {
getServices(user, (services) => {
getServiceCost(services, (cost) => {
console.log(`The service cost is ${cost}`);
});
});
});
Code language: JavaScript (javascript)
输出
Get user from the database.
Get services of john from the API.
Calculate service costs of Email,VPN,CDN.
The service cost is 300
Code language: JavaScript (javascript)
为了避免这种回调地狱问题,ES6 引入了 Promise,它允许您以更易于管理的方式编写异步代码。
首先,您需要在每个函数中返回一个 Promise
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'john'
});
}, 1000);
})
}
function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 2 * 1000);
});
}
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate service costs of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 3 * 1000);
});
}
Code language: JavaScript (javascript)
然后,您 对 Promise 进行链式调用
getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);
Code language: JavaScript (javascript)
ES2017 引入了 async
/await
关键字,它建立在 Promise 之上,允许您编写看起来更像同步代码且更具可读性的异步代码。从技术上讲,async
/ await
是 Promise 的语法糖。
如果一个函数返回一个 Promise,您可以在函数调用之前添加 await
关键字,如下所示
let result = await f();
Code language: JavaScript (javascript)
await
将等待从 f()
返回的 Promise
解决。await
关键字只能在 async
函数内使用。
以下定义了一个 async
函数,它按顺序调用三个异步操作
async function showServiceCost() {
let user = await getUser(100);
let services = await getServices(user);
let cost = await getServiceCost(services);
console.log(`The service cost is ${cost}`);
}
showServiceCost();
Code language: JavaScript (javascript)
如您所见,异步代码现在看起来像同步代码。
让我们深入了解 async / await 关键字。
async 关键字
async
关键字允许您定义一个处理异步操作的函数。
要定义一个 async
函数,您需要在函数关键字之前添加 async
关键字,如下所示
async function sayHi() {
return 'Hi';
}
Code language: JavaScript (javascript)
异步函数通过 事件循环 异步执行。它始终返回一个 Promise
。
在本例中,由于 sayHi()
函数返回一个 Promise
,您可以像这样使用它
sayHi().then(console.log);
Code language: JavaScript (javascript)
您也可以从 sayHi()
函数中显式地返回一个 Promise
,如以下代码所示
async function sayHi() {
return Promise.resolve('Hi');
}
Code language: JavaScript (javascript)
效果相同。
除了常规函数之外,您还可以在函数表达式中使用 async
关键字
let sayHi = async function () {
return 'Hi';
}
Code language: JavaScript (javascript)
箭头函数:
let sayHi = async () => 'Hi';
Code language: JavaScript (javascript)
以及类的 方法
class Greeter {
async sayHi() {
return 'Hi';
}
}
Code language: JavaScript (javascript)
await 关键字
您使用 await
关键字来等待一个 Promise
解决,无论是处于已解决状态还是处于拒绝状态。您只能在 async
函数内使用 await
关键字
async function display() {
let result = await sayHi();
console.log(result);
}
Code language: JavaScript (javascript)
在本例中,await
关键字指示 JavaScript 引擎在显示消息之前等待 sayHi()
函数完成。
请注意,如果您在 async
函数之外使用 await
运算符,您将收到错误。
错误处理
如果一个 Promise 解决,await promise
将返回结果。但是,当 Promise 被拒绝时,await promise
将抛出一个错误,就好像有一个 throw
语句一样。
以下代码
async function getUser(userId) {
await Promise.reject(new Error('Invalid User Id'));
}
Code language: JavaScript (javascript)
… 等同于以下代码
async function getUser(userId) {
throw new Error('Invalid User Id');
}
Code language: JavaScript (javascript)
在实际场景中,Promise 需要一段时间才会抛出错误。
您可以使用 try...catch
语句捕获错误,与常规 throw
语句相同
async function getUser(userId) {
try {
const user = await Promise.reject(new Error('Invalid User Id'));
} catch(error) {
console.log(error);
}
}
Code language: JavaScript (javascript)
可以捕获由一个或多个 await promise
引起的错误
async function showServiceCost() {
try {
let user = await getUser(100);
let services = await getServices(user);
let cost = await getServiceCost(services);
console.log(`The service cost is ${cost}`);
} catch(error) {
console.log(error);
}
}
Code language: JavaScript (javascript)
在本教程中,您学习了如何使用 JavaScript async
/ await
关键字编写看起来像同步代码的异步代码。