通过 ES2017 async
/await
及 Promise Combinator 实现优雅的异步代码。
以同步写异步
语法糖:async
/await
并没有为 JS 语言添加新特性,仅仅是为 Promise 提供了一种更易写易懂的语法。
JavaScript
1const wait = sec =>
2 new Promise(resolve => setTimeout(resolve, sec * 1000));
3
4// 通过 async/await 使用 Promise
5(async () => {
6 await wait(1);
7 console.log('1秒后执行');
8
9 await wait(1);
10 console.log('2秒后执行');
11
12 await wait(1);
13 console.log('3秒后执行');
14
15 await wait(1);
16 console.log('4秒后执行');
17})();
18
19// Promise 原使用方式
20// wait(1)
21// .then(() => {
22// console.log('1秒后执行');
23//
24// return wait(1);
25// })
26// .then(() => {
27// console.log('2秒后执行');
28//
29// return wait(1);
30// })
31// .then(() => {
32// console.log('3秒后执行');
33//
34// return wait(1);
35// })
36// .then(() => {
37// console.log('4秒后执行');
38//
39// return wait(1);
40// });
代码求证语法糖
JavaScript
1const asyncFunc = async () => '语法糖';
2
3console.log(asyncFunc()); // Promise {<fulfilled>: "语法糖"}
错误处理
tray...catch...finally
JavaScript
1(async () => {
2 try {
3 const postUrl = 'https://jsonplaceholder.typicode.com/posts/101';
4 const postRes = await fetch(postUrl);
5
6 if (!postRes.ok) {
7 throw new Error(`服务出错(${postRes.status})`);
8 }
9
10 const post = await postRes.json();
11
12 console.log(post);
13
14 } catch (err) {
15 console.log(err.message);
16 }
17})();
返回值
async 调用 async(推荐)
JavaScript
1const getPost = async () => {
2 try {
3 const postUrl = 'https://jsonplaceholder.typicode.com/posts/1';
4 const postRes = await fetch(postUrl);
5
6 return postRes.json();
7
8 } catch (err) {
9 // 当作为返回值使用时,需要再抛出错误,以供外部处理
10 throw err;
11 }
12};
13
14console.log('1:开始查询');
15
16(async () => {
17 try {
18 const post = await getPost();
19 console.log(`2:获取文章${post.id}`);
20
21 console.log('3:结束查询');
22
23 } catch (err) {
24 console.log(err);
25 }
26})();
27
28// 1:开始查询
29// 2:获取文章1
30// 3:结束查询
Promise 回调
JavaScript
1const getPost = async () => {
2 try {
3 const postUrl = 'https://jsonplaceholder.typicode.com/posts/1';
4 const postRes = await fetch(postUrl);
5
6 return postRes.json();
7
8 } catch (err) {
9 // 当作为返回值使用时,需要再抛出错误,以供外部处理
10 throw err;
11 }
12};
13
14console.log('1:开始查询');
15
16getPost()
17 .then(post => console.log(`2:获取文章${post.id}`))
18 .finally(() => console.log('3:结束查询'));
19
20// 1:开始查询
21// 2:获取文章1
22// 3:结束查询
Promise Combinator
组合多个 Promise 共同执行。
Promise.all()
JavaScript
1const getPost = async (postId) => {
2 const baseUrl = 'https://jsonplaceholder.typicode.com';
3 const postUrl = `${baseUrl}/posts/${postId}`;
4 const postRes = await fetch(postUrl);
5 return await postRes.json();
6};
7
8/*
9(async () => {
10 // 按顺序阻塞执行 AJAX 请求,糟糕的代码!!!
11 const post1 = await getPost(1);
12 const post2 = await getPost(2);
13 const post3 = await getPost(3);
14
15 console.log([post1.id, post2.id, post3.id]);
16})();
17*/
18
19(async () => {
20 // 并行执行 AJAX 请求
21 const posts = await Promise.all([
22 getPost(1),
23 getPost(2),
24 getPost(3)
25 ]);
26
27 const postIds = posts.map(post => post.id);
28 console.log(postIds);
29})();
Short Circuit:只要有一个 Rejected Promise,就会导致 Promise.all()
结果错误。
JavaScript
1(async () => {
2 const proAll = await Promise.all([
3 Promise.resolve('成功'),
4 Promise.reject('失败')
5 ]);
6
7 console.log(proAll); // Uncaught (in promise) 失败
8})();
Promise.race()
约定俗成:以 _
作为被忽略的变量名称,即表示在上下文中不需要的变量。
此外,
_
也是 Lodash 的默认命名空间。
JavaScript
1const getPost = async (postId) => {
2 const baseUrl = 'https://jsonplaceholder.typicode.com';
3 const postUrl = `${baseUrl}/posts/${postId}`;
4 const postRes = await fetch(postUrl);
5 return await postRes.json();
6};
7
8const timeout = sec => new Promise((_, reject) => {
9 // 约定俗成:以 `_` 表示被忽略的变量
10 setTimeout(() => reject(new Error(`请求超时${sec}秒`)), sec * 1000);
11});
12
13(async () => {
14 const post = await Promise.race([
15 getPost(1),
16 timeout(0.18)
17 ]);
18
19 console.log(post.id);
20 // 1
21 // 或
22 // Uncaught (in promise) Error: 请求超时0.18秒
23})();
Short Circuit:只要有一个 Settled Promise,就会使 Promise.race()
返回结果。
JavaScript
1(async () => {
2 const proRace = await Promise.race([
3 Promise.resolve('成功1'),
4 Promise.reject('失败1')
5 ]);
6
7 console.log(proRace); // 成功1
8})();
9
10(async () => {
11 const proRace = await Promise.race([
12 Promise.reject('失败2'),
13 Promise.resolve('成功2')
14 ]);
15
16 console.log(proRace); // Uncaught (in promise) 失败2
17})();
Promise.allSettled()
No Short Circuit:ES2020 引入,与 Promise.all()
类似,除了 Promise.allSettled()
一定会保证所有 Promise 都为 Settled 状态。
JavaScript
1(async () => {
2 const proAll = await Promise.allSettled([
3 Promise.resolve('成功'),
4 Promise.reject('失败')
5 ]);
6
7 console.log(proAll);
8 // [{...}, {...}]
9})();
Promise.any()
Short Circuit:ES2021 引入,与 Promise.race()
类似,除了 Promise.any()
只返回第一个 Fulfilled Promise,而忽略 Rejected Promise。
JavaScript
1(async () => {
2 const proAny = await Promise.any([
3 new Promise(resolve => {
4 console.log('执行:成功1');
5 resolve('成功1');
6 }),
7 new Promise((_, reject) => {
8 console.log('执行:失败1-1');
9 reject('失败1-1');
10 }),
11 new Promise(resolve => {
12 console.log('执行:成功1-1');
13 resolve('成功1-1');
14 }),
15 ]);
16
17 // 执行:成功1
18 // 执行:失败1-1
19 // 执行:成功1-1
20
21 console.log(proAny); // 成功1
22})();
23
24(async () => {
25 const proAny = await Promise.any([
26 Promise.reject('失败2'),
27 Promise.resolve('成功2'),
28 Promise.resolve('成功2-1')
29 ]);
30
31 console.log(proAny); // 成功2
32})();