介绍
Promises为我们提供了一个更容易的方式来处理我们的代码中的不同步,以序列的方式. 考虑到我们的大脑不是设计用于有效地处理不同步,这是一个非常受欢迎的补充。
使用 async 函数实现的功能可以通过将承诺与 生成器相结合来重建,但 async 函数会为我们提供我们所需的东西,而无需额外的锅炉板代码。
简单的例子
在下面的示例中,我们首先声明一个函数,该函数在 2 秒后返回一个解决到``值的承诺,然后声明一个非同步函数,然后等待承诺解决,然后将消息登录到控制台:
1function scaryClown() {
2 return new Promise(resolve => {
3 setTimeout(() => {
4 resolve('🤡');
5 }, 2000);
6 });
7}
8
9async function msg() {
10 const msg = await scaryClown();
11 console.log('Message:', msg);
12}
13
14msg(); // Message: 🤡 <-- after 2 seconds
<$>[注意] 'Wait' 是用来等待承诺解决或拒绝的新运算器,只能在同步函数内使用。
async函数的力量在涉及多个步骤时变得更加明显:
1function who() {
2 return new Promise(resolve => {
3 setTimeout(() => {
4 resolve('🤡');
5 }, 200);
6 });
7}
8
9function what() {
10 return new Promise(resolve => {
11 setTimeout(() => {
12 resolve('lurks');
13 }, 300);
14 });
15}
16
17function where() {
18 return new Promise(resolve => {
19 setTimeout(() => {
20 resolve('in the shadows');
21 }, 500);
22 });
23}
24
25async function msg() {
26 const a = await who();
27 const b = await what();
28 const c = await where();
29
30 console.log(`${ a } ${ b } ${ c }`);
31}
32
33msg(); // 🤡 lurks in the shadows <-- after 1 second
但是,在上面的例子中,每个步骤是顺序完成的,每一个额外的步骤都在等待前的步骤解决或拒绝,然后继续。
1// ...
2
3async function msg() {
4 const [a, b, c] = await Promise.all([who(), what(), where()]);
5
6 console.log(`${ a } ${ b } ${ c }`);
7}
8
9msg(); // 🤡 lurks in the shadows <-- after 500ms
Promise.all 返回了解决值的数组,一旦所有传入承诺都解决了。
在上面,我们也使用一些美好的(https://andsky.com/tech/tutorials/js-object-array-destructuring-es2015)来简化我们的代码。
回归承诺
Async 函数总是返回一个承诺,因此以下可能不会产生您想要的结果:
1async function hello() {
2 return 'Hello Alligator!';
3}
4
5const b = hello();
6
7console.log(b); // [object Promise] { ... }
由于返回的是一个承诺,你可以做这样的事情:
1async function hello() {
2 return 'Hello Alligator!';
3}
4
5const b = hello();
6
7b.then(x => console.log(x)); // Hello Alligator!
或者只是这个:
1async function hello() {
2 return 'Hello Alligator!';
3}
4
5hello().then(x => console.log(x)); // Hello Alligator!
不同形式
到目前为止,在我们的示例中,我们将async函数视为函数声明,但我们还可以定义async函数表达式和async箭头函数:
Async 函数表达式
以下是我们第一个示例中的 async 函数,但定义为函数表达式:
1const msg = async function() {
2 const msg = await scaryClown();
3 console.log('Message:', msg);
4}
Async 箭头函数
以下是同一个例子,但这一次被定义为箭头函数:
1const msg = async () => {
2 const msg = await scaryClown();
3 console.log('Message:', msg);
4}
行为错误
关于async函数的另一个非常好的事情是,错误处理也完全同步地进行,使用良好的老 ** try...catch** 陈述。
1function yayOrNay() {
2 return new Promise((resolve, reject) => {
3 const val = Math.round(Math.random() * 1); // 0 or 1, at random
4
5 val ? resolve('Lucky!!') : reject('Nope 😠');
6 });
7}
8
9async function msg() {
10 try {
11 const msg = await yayOrNay();
12 console.log(msg);
13 } catch(err) {
14 console.log(err);
15 }
16}
17
18msg(); // Lucky!!
19msg(); // Lucky!!
20msg(); // Lucky!!
21msg(); // Nope 😠
22msg(); // Lucky!!
23msg(); // Nope 😠
24msg(); // Nope 😠
25msg(); // Nope 😠
26msg(); // Nope 😠
27msg(); // Lucky!!
鉴于 async 函数总是返回承诺,您也可以像通常使用 catch 语句那样处理未处理的错误:
1async function msg() {
2 const msg = await yayOrNay();
3 console.log(msg);
4}
5
6msg().catch(x => console.log(x));
这种同步错误处理不仅在承诺被拒绝时起作用,而且在实际运行时间或语法错误发生时也起作用。在下面的示例中,第二次通过调用我们的 msg 函数,我们将传输一个数值,其原型链中没有 toUpperCase 方法。
1function caserUpper(val) {
2 return new Promise((resolve, reject) => {
3 resolve(val.toUpperCase());
4 });
5}
6
7async function msg(x) {
8 try {
9 const msg = await caserUpper(x);
10 console.log(msg);
11 } catch(err) {
12 console.log('Ohh no:', err.message);
13 }
14}
15
16msg('Hello'); // HELLO
17msg(34); // Ohh no: val.toUpperCase is not a function
Async 函数与基于承诺的 APIS
正如我们在 Fetch API的先例中所展示的那样,基于承诺的Web API是对非同步函数的完美候选人:
1async function fetchUsers(endpoint) {
2 const res = await fetch(endpoint);
3 let data = await res.json();
4
5 data = data.map(user => user.username);
6
7 console.log(data);
8}
9
10fetchUsers('https://jsonplaceholder.typicode.com/users');
11// ["Bret", "Antonette", "Samantha", "Karianne", "Kamren", "Leopoldo_Corkery", "Elwyn.Skiles", "Maxime_Nienow", "Delphine", "Moriah.Stanton"]
<$>[注] 浏览器支持: 截至2020年,全球94%的浏览器能够在 javascript中处理async/await。
结论
在 Async/Wait 函数之前, JavaScript 代码依赖于许多非同步事件(例如:对 API 进行大量调用的代码)_ 最终会导致一些人称之为回调地狱
- 一个功能和回调的链条,很难读取和理解。
Async 和 Wait 允许我们编写更清晰的非同步 JavaScript 代码。