查看原文
其他

JavaScript async / await 关键词

myfreax myfreax 2023-06-05

在本教程中,您将学习如何使用 JavaScript   async / await关键词编写异步代码。

请注意,要了解  async / await 工作原理,您需要了解 promise 的工作原理。

JavaScript async / await 关键字介绍

以前,处理异步操作,通常是使用回调函数。但是,当你嵌套很多回调函数时,代码就会更难维护。你最终会遇到一个臭名昭著的问题,称为回调黑洞。

假设您需要按以下顺序执行三个异步操作:

  1. 从数据库中获取一个用户。

  2. 从 API 获取用户的服务。

  3. 根据服务器提供的服务计算服务费用。

以下函数说明了这三个任务。请注意,我们使用 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);
}

下面展示嵌套的回调函数:

getUser(100, (user) => {
getServices(user, (services) => {
getServiceCost(services, (cost) => {
console.log(`The service cost is ${cost}`);
});
});
});

输出:

Get user from the database.
Get services of john from the API.
Calculate service costs of Email,VPN,CDN.
The service cost is 300

为了避免这种回调黑洞问题,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);
});
}

然后,您将 Promise 进行链式调用:

getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);

ES2017 引入了构建在 promises 之上的 async / await 关键字,允许您编写看起来更像同步代码并且更具可读性的异步代码。从技术上讲,async / await 是 promise 的语法糖。

如果一个函数 f 返回一个 Promise,你可以把 await 关键字放在函数调用的前面,像这样:

let result = await f();

await  将等待从 f() 函数返回的 Promise。关键字 await 只能在函数内部使用。

下面定义一个 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();

如您所见,异步代码现在看起来像同步代码。让我们深入了解 async / await 关键字。

async 关键词

async 关键字允许您定义处理异步操作的函数。要定义 async 函数,请 async 关键字放在 function 关键字前面,如下所示:

async function sayHi() {
return 'Hi';
}

异步函数通过事件轮询异步执行。它总是返回一个 Promise.

在此示例中,因为 sayHi() 函数返回一个 Promise,您可以像下面一样的 方式使用它,如下所示:

sayHi().then(console.log);

您还可以从 sayHi() 函数显式返回一个 Promise,如以下代码所示:

async function sayHi() {
return Promise.resolve('Hi');
}

效果是一样的。

除了普通函数外,您还可以在函数表达式使用  async 关键字:

let sayHi = async function () {
return 'Hi';
}

箭头函数:

let sayHi = async () => 'Hi';

类的方法:

class Greeter {
async sayHi() {
return 'Hi';
}
}

await 关键词

您使用 await 关键字等待 Promise 以解决或拒绝状态解决。您只能在 async 函数内部使用 await 关键字:

async function display() {
let result = await sayHi();
console.log(result);
}

在此示例中,await 关键字指示 JavaScript 引擎在显示消息之前等待 sayHi() 函数完成。

请注意,如果您在 async 函数外部使用 await,则会抛出错误。

错误处理

如果 await promise 已经得到解决,则返回结果。然而,当 promise 被拒绝时,将 await promise 抛出一个错误,就像有一个 throw 声明一样。

以下代码:

async function getUser(userId) {
await Promise.reject(new Error('Invalid User Id'));
}

与此相同:

async function getUser(userId) {
throw new Error('Invalid User Id');
}

在实际场景中,promise 抛出错误需要一段时间。

您可以使用 try...catch 语句来捕获 Promise 的错误,也可以是 throw 的错误:

async function getUser(userId) {
try {
const user = await Promise.reject(new Error('Invalid User Id'));
} catch(error) {
console.log(error);
}
}

可以捕获由一个或多个 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);
}
}

结论

在本教程中,您学习如何使用 JavaScript async / await关键字编写看起来像同步代码的异步代码。

如果您有任何问题或反馈,请随时发表评论。点击下方阅读原文获取更好排版格式与文章参考引用

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存