JS -- (22) async、await

123 阅读4分钟

一. async 异步函数

async ----async关键字用来声明异步函数 ,*可以让函数有异步特征,但函数仍然是同步求值的。* 异步函数仍具有 JS 普通函数的特性!!**本质上**,async`函数就是 Generator 生成器函数的语法糖。它的实现原理,就是将 Generator 函数和它的自动执行器包装在一个函数里。

栗子:

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return co(function* () {
    // ...
  });
}

async 函数能实现的效果,都可以用 Promise 的 then() 链来进行实现。它就是为了优化 then() 链而开发来的!!

// 使用 generator
var fetch = require('node-fetch');
var co = require('co');

function* gen() {
    var r1 = yield fetch('https://api.github.com/users/github');
    var json1 = yield r1.json();
    console.log(json1.bio);
}

co(gen);
// 使用 async
var fetch = require('node-fetch');

var fetchData = async function () {
    var r1 = await fetch('https://api.github.com/users/github');
    var json1 = await r1.json();
    console.log(json1.bio);
};

fetchData();

async 函数返回的是一个 Promise 对象【异步函数的返回值会被 Promise.resolve() 包装后再返回】,所以在 async 函数的返回值上调用 Promise 原型上的方法!!!

async function fn(){
 return 1;
}
fn().then(()=>{})

二. await 关键字

await 是等待的意思,async 用来声明该函数是异步的,而 await用于等待一个异步操作的执行完成。【awiat = yield + 自动执行函数co()】 await只能出现在 async 函数中

1> await到底在等什么?

  • await 等待的是一个表达式【可以是 Promise 对象,也可以是任意其他值】

2> 遇到 await 关键字后?【跟 await 后面的表达式是什么值没有关系】

  • 暂停执行 await 后面的代码,并记录在哪停,退出异步函数,让出主线程。
  • 等该异步函数外部的同步代码执行完毕后,才会重新恢复执行该异步函数!!

三. async/await 的优势(相比于 Promise)

2168.png

<1> 代码更加简洁 && 传递中间值更优雅

栗子1:

function fetch() {
  return fetchData()
  .then(data => {
    if (data.moreData) {
        return fetchAnotherData(data)
        .then(moreData => {
          return moreData
        })
    } else {
      return data
    }
  });
}
// 等价于
async function fetch() {
  const data = await fetchData()
  if (data.moreData) {
    const moreData = await fetchAnotherData(data);
    return moreData
  } else {
    return data
  }
};

栗子2:

function fetch() {
  return (
    fetchData()
    .then(value1 => {
      return fetchMoreData(value1)
    })
    .then(value2 => {
      return fetchMoreData2(value2)
    })
  )
}

async function fetch() {
  const value1 = await fetchData()
  const value2 = await fetchMoreData(value1)
  return fetchMoreData2(value2)
};
<2> async/await 的出现解决了 Promise 吃掉错误的问题
function fetch() {
  try {
    fetchData()
      .then(result => {
        const data = JSON.parse(result)
      })
      .catch((err) => {
        console.log(err)
      })
  } catch (err) {
    console.log(err)
  }
}

如上,如果是 JSON.parse(result) 出错了,那么此时的 try/catch 是捕获不到的,因为JSON.parse(result)在 Promise 内部!!!但是使用了async/await就可以了!!

async function fetch() {
  try {
    const data = JSON.parse(await fetchData())
  } catch (err) {
    console.log(err)
  }
};
<3> 调式方面,更简单!!

四. 使用 async 可能会出现的问题 ---- "async 地域"

async地域 指的是开发者贪图语法上的简洁而让原本可以并行执行的内容变成了顺序执行,从而影响了性能。如下:

栗子1:

(async () => {
  const getList = await getList();
  const getAnotherList = await getAnotherList();
})();

getList() 和 getAnotherList() 其实并没有依赖关系,但是现在的这种写法,虽然简洁,却导致了 getAnotherList() 只能在 getList() 返回后才会执行,从而导致了多一倍的请求时间。修改如下:

(1) 方法1:改变等待内容,从等待函数执行完毕===>等待函数请求完成后的结果

(async () => {
  const listPromise = getList();
  const anotherListPromise = getAnotherList();
  await listPromise;
  await anotherListPromise;
})();

(2) 方法2:使用 Promise.all()

(async () => {
  Promise.all([getList(), getAnotherList()]).then(...);
})();

栗子2:【存在依赖关系的】【写在不同的 async 函数中】

(async () => {
  const listPromise = await getList();
  const anotherListPromise = await getAnotherList();

  // do something

  await submit(listData);
  await submit(anotherListData);

})();

getList() 和 getAnotherList() 其实并无依赖,submit(listData) 和 submit(anotherListData) 也没有依赖关系。But submit(listData) 需要在 getList() 之后,submit(anotherListData) 需要在 anotherListPromise() 之后。

  • 将互相依赖的语句包裹在同一个 async 函数中
async function handleList() {
  const listPromise = await getList();
  // ...
  await submit(listData);
}

async function handleAnotherList() {
  const anotherListPromise = await getAnotherList()
  // ...
  await submit(anotherListData)
}
  • 并发执行 async 函数
async function handleList() {
  const listPromise = await getList();
  // ...
  await submit(listData);
}

async function handleAnotherList() {
  const anotherListPromise = await getAnotherList()
  // ...
  await submit(anotherListData)
}

// 方法一 : 等待函数调用返回的结果
(async () => {
  const handleListPromise = handleList()
  const handleAnotherListPromise = handleAnotherList()
  await handleListPromise
  await handleAnotherListPromise
})()

// 方法二 :用 Promise.all()
(async () => {
  Promise.all([handleList(), handleAnotherList()]).then()
})()

五. async 的一些讨论

<1> async 会取代 Generator 吗?

async 其实就是 Generator 的语法糖,在异步操作方面,async 可以取代 Generator。但是 async 和 Generator 两个语法本身是用来解决不同的问题的。

<2> async 会取代 Promise 吗?

目前不会!!!

  • async 函数返回的是一个 Promise 对象
  • 面对复杂的异步流程,Promise 提供的 all 和 race 会更加好用【实现并发执行】
  • Promise 本身是一个对象,所以可以在代码中任意传递
  • async 的支持率还很低,即使有 Babel,编译后也要增加 1000 行左右。

参考1

参考2