随着async/await
语法糖的发布,使用同步写法写异步代码一定会成为趋势。之前Promise
使用的then
、catch
控制流程,
换用新的语法糖之后,要重新拾起try...catch
了。
try…catch 基本用法
一个例子
1 | function hello() { |
输出:
1 | 1 |
finally
代码执行后输出了5
、8
说明了finally
的功能,与Promise
中的finally
类似:无论上面的流程执行的如何,finally
中的代码都会执行。
catch
代码执行后输出了4
、error message of function hello: 2
,说明了catch
的功能,与Promise
中的catch
类似:
只要try
里面的代码中抛出了异常,catch
都能捕获到异常。
但是,为什么3
没有输出呢?为什么6
输出了?
Error
如果代码执行出现了错误,代码是不会继续向下执行的,所以3
没有输出。
而如果错误被捕获了,并且处理了,代码就会继续执行,所以,6
输出了。
但这里有一个问题,我们有时候不希望6
被输出,因为之前代码已经出错了,6
就不应该被输出。
try..catch 嵌套
上面的问题之所以出现,是因为我们使用了嵌套的try...catch
,也与promise
的then catch
类似,嵌套在内部的函数(hello),如果发生异常,会被,内部函数的catch
捕获,捕获后,并不会继续抛出让外部函数(world)继续捕获。如果需要,我们必须在内部函数的catch
中,继续抛出这个错误。
修改代码如下:
1 | function hello() { |
输出:
1 | 1 |
Bingo! 代码如我们希望中的样子执行了!异常被外部函数(world)捕获了,6
没有被输出。
说完try...catch
控制流程,我们看看它如何处理async/await
的异步流程控制。
async/await
Promise 写法 VS async/await 写法
Promise
写法
1 | function hello() { |
async/await
写法
1 | async function hello() { |
看出来区别了吗?其实感觉没多大区别,除了代码少了 4 行,链式写法变成了同步写法。
但是,如果整个程序流程再复杂三倍呢?
我会义无反顾的选择async/await
!
并行和串行
TODO
最佳实践
经过一段时间的使用,对于async/await
的正确使用,我总结了一下几点:
必须使用
try...catch
。确保正确的流程控制。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// resolve
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ state: 1 }), 1000);
});
}
// reject
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => reject({ error: 2 }), 1000);
});
}
async function hello() {
let data = await getData();
console.log(data.state);
}
async function hello() {
try {
let data = await getData();
console.log(data.state);
} catch (e) {
console.log(e);
let data = {};
}
}
hello();如果
await
后面的promise
返回的是reject
,那么下面的同步代码不会执行。所以必须使用try...catch
,如果出现这种情况,
会进入catch
,执行错误处理代码。多重嵌套时,内层的
try...catch
必须有返回值,且catch
中的返回值是Promise.reject
。返回值是promise
。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// resolve
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ state: 1 }), 1000);
});
}
// reject
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => reject({ error: 2 }), 1000);
});
}
async function hello() {
try {
return await getData();
} catch (e) {
console.log(`${e} --- hello catch`);
return Promise.reject(e);
// return Promise.resolve(e)
}
}
async function world() {
try {
// 不会执行
data = await hello();
} catch (e) {
data = {};
console.log(`${e} --- world catch`);
}
}
world();world
使用异步方式调用了hello
,如果hello
中的getData
方法抛出了错误,会进入hello
里的catch
,则catch
中也必须有返回值,否则,将不会进入world
中的catch
,而是继续执行try
里面的代码;如果使用了Promise.resolve
返回内容或者返回内容不是Promise
,就也是执行try
里面的代码;只用返回的结果是Promise
且为Promise.reject
,才会正常的进入world
中的catch
。vue 的生命周期是独立的,不会受到异步函数的影响。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20export default {
data() {
return {
data: null
};
},
async created() {
this.data = await this.getData();
},
mounted() {
console.log(this.data);
},
methods: {
getData() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve({ state: 1 }), 1000);
});
}
}
};我们知道
created
函数会早于mounted
被调用,但是mounted
并不会等待created
中的异步函数,vue
的生命周期只跟其内部的执行流程有关,不受外界的影响。
总结
就如我一开始所说,async/await
并不是什么新的技术,而只是语法糖。
但是,如果要替代现在的promise
,特别是在多人团队中推广,我还是建议先去学习一下语法,尤其是异常处理和多层嵌套下向外抛出异常,不然,….说多了都是坑 😂。
这篇文章只是讲了async/await
的具体实践,如果你想了解promise
和async/await
的实现原理,请移步Promise实现原理 🌑 从Promise到async/await 🌑。