1.介绍

  • 它是使用了 Generator 函数基于 Promise 的封装,是 Promise 的一个语法糖;
  • 它是一种异步编程的解决方案,可以以同步的代码方式来写异步;
  • await 关键字可以“暂停”async function 的执行;
  • 可以用 try-catch 捕获到 async function 所得到的错误;

2.基本使用

声明两个 promise 对象:

1
2
3
4
5
6
7
8
9
10
11
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 500);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2);
}, 500);
});
复制代码;

传统的方式:

1
2
3
4
5
6
7
promise1.then((res) => {
console.log(res); // 1
});
promise2.catch((res) => {
console.log(res); // 2
});
复制代码;

async await 方式

1
2
3
4
5
6
7
8
9
10
11
async function asyncFunc() {
const res = await promise1;
console.log(res); // 1
try {
const res = await promise2;
} catch (err) {
console.log(err); // 2
}
}
asyncFunc();
复制代码;

3.进阶使用

场景:要做三件事,下一件事依赖上一件事返回的结果;
假设:dosomething 返回的是 Promise;
处理顺序:dosomething1 => dosomething2 => dosomething3;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 传统的方式 (可以看到存在多重嵌套,错误处理也需要单独写)
dosomething1().then((res1) => {
dosomething2(res1).then((res2) => {
dosomething3(res2).then((res3) => {
console.log(res3);
});
});
});
// async await方式 (没了嵌套,简洁了)
try {
const res1 = await dosomething1();
const res2 = await dosomething1(res1);
const res3 = await dosomething1(res2);
console.log(res3);
} catch (error) {
console.log(error); // 统一捕获错误
}
复制代码;

注意,如果每件事没有相互之间的依赖,使用了上面的那种方式后,会增加得到结果的时间(明明可以并行处理的,但是变成了串行);可以考虑使用 Promise.all 来执行:

1
2
3
4
5
6
const [res1, res2, res3] = await Promise.all([
dosomething1(),
dosomething2(),
dosomething3(),
]);
复制代码;

4.原理解读

Generator 函数

Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,通过 next()方法可以切换到下一个状态,可以控制代码执行流程(暂停和继续),从而为异步编程提供解决方案。
基本使用:

1
2
3
4
5
6
7
8
9
10
function* myGenerator() {
yield "1";
yield "2";
return "3"; // 到return这步,done为true;
}
const gen = myGenerator(); // 获取迭代器
gen.next(); //{value: "1", done: false}
gen.next(); //{value: "2", done: false}
gen.next(); //{value: "3", done: true}
复制代码;

可以通过给 next()传参, 让 yield 具有返回值:

1
2
3
4
5
6
7
8
9
10
11
12
function* myGenerator() {
console.log(yield "1"); //res1
console.log(yield "2"); //res2
console.log(yield "3"); //res3
}
// 获取迭代器
const gen = myGenerator();
gen.next();
gen.next("res1");
gen.next("res2");
gen.next("res3");
复制代码;

await async 的规范

  • async 函数会自动返回一个 Promise 对象;
  • await 关键字能够返回 Promise 的 resolve 的值;
  • await 关键字必须用作 async 函数内;

实现思路

  • 相同:可以看到 */yield 和 async/await 这两个关键词有点类似的;
  • 不同:await 每一步可以自动执行,不需要手动调用 next()就能自动执行下一步;
  • 不同:asnyc 返回的是一个 Promise, Generator 函数返回的是一个迭代器对象;

所以我们需要封装一个返回 Promise 对象的并且可以自动执行的 Generator 函数的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//通过包一层runAsync函数 模拟await async
function* asyncFn() {
try {
var a = yield 1; // next()返回 { value: 1, done: false }
var b = yield Promise.resolve(++a); // next()返回 { value: Promise.resolve(++a), done: false }
//throw Error();
var c = yield Promise.resolve(++b); // next()返回 { value: Promise.resolve(++b), done: false }
console.log(c); // 3
return c; // next()返回 { value: 3, done: false }
} catch (error) {
console.log(error,"error")
}
}
function runAsync(asyncFn) {
let g = new asyncFn();
// async返回的是一个Promise
return new Promise((resolve, reject) => {
// 实现自动执行的方法
function _next(val) {
try {
var res = g.next(val); //得到Generator对象: {value: xxx, done: xxx}
} catch (error) {
// 防止yield执行过程成抛出错误
reject(error);
return;
}
// 执行到最后;退出自动执行
if (res.done) return resolve(res.value);

// 自动执行下一个yield
// 包一层Promise是为了兼容yield后面不是跟Promise对象的情况;
Promise.resolve(res.value).then(
(data) => {
_next(data);
},
(err) => {
g.throw(err); // 给外面的try catch捕获
}
);
}
_next();
});
}
// 执行asyncFn,模拟的async await函数执行流程;
let p = runAsync(asyncFn);
p.then(res => {
console.log(res) // 3
}).catch((err) => {
console.log(err);
});
复制代码