了解

ES7 提出的async 函数,让 JavaScript 对于异步操作有了终极解决方案;async函数实际上是Generator函数的语法糖,使用关键字async来表示,在函数内部使用await来表示异步,相较于Generator,有以下改进:

  • 内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
  • 更好的语义。async 和 await 相较于 * 和 yield 更加语义化
  • 更广的适用性。co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise对象。而 async 函数的 await 命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)
  • 返回值是 Promise。async 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用

为什么使用

一个函数如下:

  1. function fetchUser() {
  2. return new Promise((resolve, reject) => {
  3. axios('http://xxx/XXX')
  4. .then((data) => {
  5. resolve(data.json());
  6. }, (error) => {
  7. reject(error);
  8. })
  9. });
  10. }

使用promise调用如下:需要使用then函数,如果有多重调用,虽然不会出现回调地狱,但是出现多层嵌套的情况,语义化不明显,代码流出也不能很好的表示执行。。

  1. /**
  2. * Promise 方式
  3. */
  4. function getUserByPromise() {
  5. fetchUser()
  6. .then((data) => {
  7. console.log(data);
  8. }, (error) => {
  9. console.log(error);
  10. })
  11. }
  12. getUserByPromise();

使用async方式,能够完美解决多层嵌套问题,并且语义明显、流程清晰

  1. /**
  2. * async 方式
  3. */
  4. async function getUserByAsync(){
  5. let user = await fetchUser();
  6. return user;
  7. }
  8. getUserByAsync()
  9. .then(v => console.log(v));

实际使用

初步使用

  • 每一个async函数在执行之后,都会自动返回一个Promise对象:
  1. 如果我们手动在async函数中使用return关键字返回一个直接量,async就会把这个直接量通过Promise.resolve()封装成Promise对象
  2. 如果async函数没有返回值,则它返回Promise.resolve(undefined)
    1. async test() {
    2. // 什么都不写
    3. }
    4. async test1() {
    5. let rel = await Promise.resolve('success')
    6. return rel
    7. }
    8. let result = test()
    9. let result1 = test1()
    10. console.log(result) // 打印出Promise对象
    11. console.log(result1) // 打印出Promise对象
  • await必须在async函数里面使用,否则会报错
  • await后面需要跟Promise对象,不然就没有意义,而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数,如下图:

image.png

错误处理

await可以直接获取到后面Promise成功状态传递的参数,但是却捕捉不到失败状态;如果第一个await发生错误,后面的await将形成阻塞。所以,我们可以通过两种方式来进行错误捕捉
1、因为async返回的原本就是一个Promise对象,所以我们可以直接在调用的时候跟一个catch回调即可

  1. // 紧跟上面例子
  2. test1().then(response => {
  3. console.log(response)
  4. }).catch(error => {
  5. // 捕捉异常
  6. console.log(error)
  7. })

2、在await外面包裹一层try…catch,进行错误的捕捉

  1. try {
  2. let result = await performanceApi.getPerformanceManageList(params)
  3. let {data} = result
  4. } catch (error) {
  5. // 异常捕捉
  6. console.log(error)
  7. }

3、可能有用,可以了解:使函数同时返回两个值,如下:

  1. async function test () {
  2. try {
  3. let res = await asyncFun()
  4. return [null, res]
  5. } catch (e) {
  6. return [e, null]
  7. }
  8. }
  9. async function func() {
  10. let [err, res] = await errorCaptured(asyncFunc)
  11. if (err) {
  12. //... 错误捕获
  13. }
  14. //...
  15. }

Async/await的陷阱

虽然await让你的代码看起来想同步的,但是一定要记住,这些方法其实还是在以异步的方式执行,以下代码使没有必要的:

  1. async test(authorId) {
  2. // 这样写就意味着:fun1执行完,才会执行fun2,而fun1和fun2是没有依赖关系的,这样执行会花费多余的时间,不合理!
  3. const rel1 = await fun1()
  4. const rel2 = await fun2()
  5. return {
  6. rel1,
  7. rel2
  8. }
  9. }

应该修改为,异步执行,没有顺序

  1. async test(authorId) {
  2. // 提前执行
  3. const relPromise = fun1()
  4. const relsPromise = fun2()
  5. const rel1 = await relPromise
  6. const rel2 = await relsPromise
  7. return {
  8. rel1,
  9. rel2
  10. }
  11. }

兼容性image.png