JavaScript中的Async/Await

Posted by cl9000 on April 05, 2020

达到完美境界并不是无以复加,而是无可去除。——<安托万·德·圣·埃克苏佩里>

作者:Ashish Lahoti
译者:cl9000
来源:https://codingnconcepts.com/javascript/async-await-in-javascript/

Async 函数和 Await 关键字是 ECMAScript 2017 发布的最新 JavaScript 插件,它引入了一种编写异步函数的新方法。在这篇文章中,我们将讨论为什么我们应该使用 async/wait,它的语法和实例的实际用法。

为什么 Async/Await?

在早期,您可以使用回调来处理异步操作。然而,回调函数的功能有限,并且经常会导致难以管理的代码。如果你要处理多个异步调用,它会导致大量嵌套的回调代码,这也被称为回调地狱。

后来 ES6 中引入了Promise,以克服回调函数的问题并提高代码的可读性。最后,在 ES2017 中引入了 Async/Await,它只不过是Promise 的语法改进版本。它的底层是 Promise改进了语法,

  • 链式 Promise 和在链式 Promise之间传递值的更好方法
  • Promise相比,代码更加简洁易读
  • 调试是很容易的
  • 更好的错误处理

语法

async

async关键字应用于函数之前时,它会转换为异步函数并始终返回 Promise 对象。

1
2
3
4
5
6
7
async function hello() {
//return Promise.resolve("Hello");
return "Hello";
}

console.log(hello());
hello().then(data => console.log(data));

Output
Promise {: “Hello”}
Hello

在上面的代码片段中,我们看到当我们执行 async 函数 hello() 时,它将字符串值包装在 Promise对象中,并返回一个解析后的 Promise。我们也可以显式返回已解析的 Promise对象,第2行和第3行是相同的。

然后我们进一步在已解析的 promise 对象上使用它来获取 Hellos字符串(第7行)

await

1
2
// works only inside async functions
let value = await promise;

await 关键字只在 async 函数内部有效,它使函数的执行等待,直到返回的 promise稳定下来( resolvereject )。

1
2
3
4
5
6
7
8
9
10
11
async function hello() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Hello"), 5000)
});

let value = await promise; // wait until the promise resolves

return value;
}

hello().then(data => console.log(data));

请注意,在上面的代码片段中,当我们执行异步函数 hello() 时,函数的执行在第6行等待5秒,然后返回已解析的 promise对象。CPU资源在此等待期间没有被利用,可以用于其他工作。

还要注意的是,如果你在非异步函数中使用 await关键字,它会像下面这样返回 SyntaxError:

1
2
3
4
5
function hello() {
let promise = Promise.resolve("Hello");
let value = await promise; ⓧ Uncaught SyntaxError: await is only valid in async function
return value;
}

用法

我们来看一个使用 fetch API从多个 HTTP 端点获取数据的实际例子。

1. 创建三个promise对象

我们已经创建了一个公共函数 getData,并使用它来创建三个参数化的 Promise 对象 getUsergetPostsgetComments,以从它们各自的 HTTP 端点获取数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//create a common getData function
let getData = (url) => new Promise(function (resolve, reject ){
fetch(url)
.then(response => {
return response.json();
})
.then(data => {
resolve(data);
})
.catch(error => {
reject(error);
});
});

//create multiple promises from common getData function
let getUsers = getData('https://jsonplaceholder.typicode.com/users');
let getPosts = (userId) => getData(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`);
let getComments = (postId) => getData(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`);

2. 链式 Promise

我们的目标是获取第一个用户在第一篇文章上的所有评论。

我们首先从 getUsers promise中获取所有用户,并通过传递 firstUser 将其与getPost promise 链接起来。通过传递 firstPost,进一步将它与 getComments promise 链接起来。

1
2
3
4
5
6
7
8
9
10
//promise chaining of multiple asynchronous calls
getUsers.then(users => {
let firstUser = users[0];
return getPosts(firstUser.id);
}).then(posts => {
let firstPost = posts[0];
return getComments(firstPost.id);
}).then(comments => {
console.log(comments);
}).catch(error => console.error(error));

Output
▼ (5) [{…}, {…}, {…}, {…}, {…}]
➤ 0: {postId: 1, id: 1, name: “id labore ex et quam laborum”, email: "Eliseo@gardner.biz", body: “laudantium enim quasi est quidem magnam voluptate …utem quasi↵reiciendis et nam sapiente accusantium”}
➤ 1: {postId: 1, id: 2, name: “quo vero reiciendis velit similique earum”, email: "Jayne_Kuhic@sydney.com", body: “est natus enim nihil est dolore omnis voluptatem n…iatur↵nihil sint nostrum voluptatem reiciendis et”}
➤ 2: {postId: 1, id: 3, name: “odio adipisci rerum aut animi”, email: "Nikita@garfield.biz", body: “quia molestiae reprehenderit quasi aspernatur↵aut …mus et vero voluptates excepturi deleniti ratione”}
➤ 3: {postId: 1, id: 4, name: “alias odio sit”, email: "Lew@alysha.tv", body: “non et atque↵occaecati deserunt quas accusantium u…r itaque dolor↵et qui rerum deleniti ut occaecati”}
➤ 4: {postId: 1, id: 5, name: “vero eaque aliquid doloribus et culpa”, email: "Hayden@althea.biz", body: “harum non quasi et ratione↵tempore iure ex volupta…ugit inventore cupiditate↵voluptates magni quo et”}
length: 5
proto: Array(0)

3. async/await

我们使用 async/await 实现获取评论同样的目标

1
2
3
4
5
6
7
8
9
10
11
  //async and await makes code cleaner and readable
async function getCommentsOfFirstPostByFirstUser(){
let users = await getUsers;
let firstUser = users[0];
let posts = await getPosts(firstUser.id);
let firstPost = posts[0];
let comments = await getComments(firstPost.id);
return comments;
}

getCommentsOfFirstPostByFirstUser().then(comments => console.log(comments));

Output
▼ (5) [{…}, {…}, {…}, {…}, {…}]
➤ 0: {postId: 1, id: 1, name: “id labore ex et quam laborum”, email: "Eliseo@gardner.biz", body: “laudantium enim quasi est quidem magnam voluptate …utem quasi↵reiciendis et nam sapiente accusantium”}
➤ 1: {postId: 1, id: 2, name: “quo vero reiciendis velit similique earum”, email: "Jayne_Kuhic@sydney.com", body: “est natus enim nihil est dolore omnis voluptatem n…iatur↵nihil sint nostrum voluptatem reiciendis et”}
➤ 2: {postId: 1, id: 3, name: “odio adipisci rerum aut animi”, email: "Nikita@garfield.biz", body: “quia molestiae reprehenderit quasi aspernatur↵aut …mus et vero voluptates excepturi deleniti ratione”}
➤ 3: {postId: 1, id: 4, name: “alias odio sit”, email: "Lew@alysha.tv", body: “non et atque↵occaecati deserunt quas accusantium u…r itaque dolor↵et qui rerum deleniti ut occaecati”}
➤ 4: {postId: 1, id: 5, name: “vero eaque aliquid doloribus et culpa”, email: "Hayden@althea.biz", body: “harum non quasi et ratione↵tempore iure ex volupta…ugit inventore cupiditate↵voluptates magni quo et”}
length: 5
proto: Array(0)

总结

我们看到 async/awaitpromise 更容易使用。

参考

关注【公众号】,了解更多。
关注【公众号】,了解更多。
关注【公众号】,了解更多。



支付宝打赏 微信打赏

赞赏一下 坚持原创技术分享,您的支持将鼓励我继续创作!