async 函数

async 函数是 Generator 函数的语法糖,使得我们可以用同步的方式用于处理异步任务

  function fetch(time, sue = true){
    return new Promise((resolve, reject) => {
      let callback = sue ? resolve : reject
      setTimeout(() => {
        callback('ye~')
      }, time)
    })
  }
  fetch(1000)
  .then(res => {
      console.log(res)
  })
  // async 写法
  const res = await fetch(1000)
  console.log(res)

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数

  async function asy(){
    const res = await fetch(1000)
    return res
  }
  asy()
  .then(res => {
      console.log('async', res)
    })

错误处理

处理 async 函数错误有几个几种:

调用async函数时使用catch捕获错误

async 函数内部抛出错误,会导致返回的 Promise 对象变为 reject 状态。抛出的错误对象会被 catch 方法回调函数接收到

function fetch(time, sue = true, info){
    return new Promise((resolve, reject) => {
      let callback = sue ? resolve : reject
      setTimeout(() => {
        callback(info)
      }, time)
    })
  }

  async function asy(){
    const res = await fetch(1000, false, '给你一个错')
    return res
  }
  asy()
  .catch(err => {
      console.log('async', err)
    })
    // async 给你一个错

try...catch

  async function asy(){
    try{
      const res = await fetch(1000, false, '给你一个错')
    } catch(err){
      console.log('err', err)
    }
  }
  asy()

如果错误已经被异步函数的 catch 捕获, 错误将不会冒泡到 async 函数

async function asy(){
const res = await fetch(1000, false, '给你一个错')
    .catch(err => {
    console.log('我在里面处理了错误', err)
    return 'OK'
    })
    console.log('async', res)
}
asy()
// 我在里面处理了错误 给你一个错

// async OK

注意点

await 命令只能用在 async 函数之中,如果用在普通函数,就会报错

  function fetch(time, sue = true, info){
    return new Promise((resolve, reject) => {
      let callback = sue ? resolve : reject
      setTimeout(() => {
        callback(info)
      }, time)
    })
  }

  let docs = [fetch, fetch, fetch];

  // 可能得到错误结果
  docs.forEach(async function (doc, index) {
    const res = await doc(1000, true, 'ye~');
    console.log('index', res)
  });
  // 1秒后同时输出 3个 index ye~

上面代码不会像预期那样的工作,原因是 forEach 内部循环了三个 async 函数,这时三个 doc 操作将是并发执行,也就是同时执行。正确的写法是采用 for 循环

let docs = [fetch, fetch, fetch];
  

async function doAs(){
  for (let doc of docs) {
    const res = await doc(1000, true, 'ye~');
    console.log(res)
  }
}
doAs()

如果确实希望多个请求并发执行,可以使用 Promise.all 方法。当三个请求都会 resolved 时,下面两种写法效果相同

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的写法

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}

async 函数的实现原理

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数

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

// 等同于

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

所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。

下面给出 spawn 函数的实现,基本就是前文自动执行器的翻版

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}
Last Updated:
Contributors: 156081289@qq.com