异步函数

异步函数 是 ES2017 引入的新特性,它是使用async关键字定义的函数,其中的异步操作使用await关键字来等待其异步操作完成。异步函数将普通函数转换为异步函数,可以使代码更加易于理解和维护。

async function getData(){
    const response = await fetch('https://example.com/data')
    const data = await response.json();
    return data;
}  

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

await 关键字【放在异步操作的前面】

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

1> await到底在等什么?
  • await 等待的是一个表达式【可以是 Promise 对象,也可以是任意其他值】
2> 遇到 await 关键字后?【跟 await 后面的表达式是什么值没有关系】
  • 暂停执行 await 后面的代码,并记录在哪停,退出异步函数,让出主线程

  • 等该异步函数外部的同步代码执行完毕后,才会重新恢复执行该异步函数!!

异步函数 and Promise

异步函数所实现的效果都可以用 Promise 实现。

异步函数相对于 Promise 的优势:

(1) 代码更加简洁易读【代替了 then() 链式调用】
function fetch() {
  return fetchData()
  .then(data => {
    if (data.moreData) {
        return fetchAnotherData(data)
        .then(moreData => {
          return moreData
        })
    } else {
      return data
    }
  });
}
// 等价于
async function(){
  const data = await fetchData();
  if (data.moreData) {
    const moreData = await fetchAnotherData(data);
    rerturn moreData;
  }else{
    return data;
  }
}
(2) 错误处理友好,解决了 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) 
   } 
 };

异步函数的劣势:可能会出现 async地域

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

栗子1:

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

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

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

ini
复制代码
(async () => {
  const listPromise = getList();
  const anotherListPromise = getAnotherList();
  await listPromise;
  await anotherListPromise;
})();

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

scss
复制代码
(async () => {
  Promise.all([getList(), getAnotherList()]).then(...);

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

scss
复制代码
(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() 之后。

*(1)方法一: 将互相依赖的语句包裹在同一个 async 函数中

csharp
复制代码
async function handleList() {
  const listPromise = await getList();
  // ...
  await submit(listData);
}
async function handleAnotherList() {
  const anotherListPromise = await getAnotherList()
  // ...
  await submit(anotherListData)
}

async 会代替 Promise 么?

目前不会!!!

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

异步函数和生成器 Generator

异步函数(Async Functions)是使用async关键字定义的函数,它可以在函数体内使用await关键字来暂停执行并等待一个 Promise 的解决。异步函数总是返回一个 Promise 对象。

生成器(Generators)是一种特殊类型的函数,它通过使用function*声明来定义。生成器使用yield关键字来暂停函数的执行并生成一个值,然后可以通过调用next()方法来继续执行函数并返回下一个生成的值。

async函数其实就是 PromiseGenerator语法糖。它的实现原理,就是将 Generator 函数和它的自动执行器包装在一个函数里,并返回 Promise 实例对象。

  • Generator 里的 yield 关键字可以实现在函数体内指示函数的执行和暂停,那就是在 异步函数 里遇到异步操作就暂停挂起,异步操作执行完毕后用自动执行器自动执行next()函数进行下一步操作!!!

  • 而且通过将异步函数和生成器相结合,我们可以更加优雅地处理异步操作的控制流程

async function* asyncGenerator() {
  yield await someAsyncFunction1();
  yield await someAsyncFunction2();
  // ...
}
async function processData() {
  for await (const data of asyncGenerator()) {
    // 处理数据
  }
}