ES6 async

102 阅读3分钟

基本概念

  • Generator 函数的语法糖
const { promisify } = require("util");
const path = require('path')
const file1 = path.join(__dirname, './text/1.txt')
const file2 = path.join(__dirname, './text/2.txt')
const readFileP = promisify(readFile)

function* f() {
  let data1 = yield readFileP(file1)
  console.log('耶,完成了1,数据是' + data1);
  let data2 = yield readFileP(file2)
  console.log('耶,完成了2,数据是' + data2);
}

//async函数的版本
async function f() {
  let data1 = await readFileP(file1)
  console.log('耶,完成了1,数据是' + data1);
  let data2 = await readFileP(file2)
  console.log('耶,完成了2,数据是' + data2);
}

  • async函数的版本就是将 Generator 函数的星号(*)替换成async,将yield替换成await

定义async函数

  • 用async关键字定义async函数
async function f() {
  let data1 = await readFileP(file1)
  console.log('耶,完成了1,数据是' + data1);
  let data2 = await readFileP(file2)
  console.log('耶,完成了2,数据是' + data2);
}

执行async函数

  • 相当于执行了自动运行的Generator函数

  • async函数如果返回的结果不是Promise,则会运行结果包装成一个Promise返回

async function f() {
  console.log(1);
}
f().then(()=>{
  console.log(2);
})

async function f() {
  console.log(1);
  return 'done'
}

f().then(value => {
  console.log(value);
})

await关键字

  • async函数执行的过程中,每次遇到await关键字,会将控制权转回外部环境
  1. await后面是Promise实例

    • 等到 Promise实例被resolve后,把本次await到下次await之间的代码推到MircoTask(微任务)中等待执行

    • await的返回值是该Promise实例resolve的值

  2. await后面不是Promise实例

    • 立即将本次await到下次await之间的代码推到MircoTask(微任务)中等待执行

    • await的返回值是等于await后面表达式的值

// `await`后面是Promise 实例
async function f() {
  let data = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('a')
    }, 2000)
  })
  console.log(data);
}

//f()
//console.log('end')
// `await`后面不是Promise 实例
async function f() {
  let data = await 'a'
  console.log(data);
}
f()
console.log('end'); 
//end
//a

async函数的错误处理

  • try..catchawait进行错误捕捉

    • Promise被reject或抛出错误,await之后的代码不会执行
async function f() {
  try {
    let data = await new Promise((resolve, reject) => {
      setTimeout(() => {
        reject('123')
      }, 2000)
    })
    //后续代码无法执行
    console.log('done');
  }catch (e) {
    console.log('发生错误:',e);
  }
}
f()

async函数处理并发异步任务

  • 并发执行,可以使用Promise.all
/*并发处理异步*/
async function f() {
  let time1 = new Date()
  let [data1,data2] = await Promise.all([
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('123')
      }, 2000)
    }),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('123')
      }, 3000)
    })
  ])
  console.log(data1,data2,'用时:'+ (new Date() - time1));
}
f()

async函数与Promise的对比

  • async更加简洁

  • 在处理不同异步结果相互依赖

  • 错误处理

  • if...else分支等情况时更加简便

const {readFile} = require('fs')
const { promisify } = require("util");
const path = require('path')
const file1 = path.join(__dirname, './text/1.txt')
const file2 = path.join(__dirname, './text/2.txt')
const file3 = path.join(__dirname, './text/3.txt')
const readFileP = promisify(readFile)

function f1() {
  readFileP(file1).then(data1 =>{
    console.log('耶,完成了1,数据是' + data1);
    return readFileP(file2)
  }).then(data2 => {
    console.log('耶,完成了1,数据是' + data2);
    return readFileP(file3)
  }).then(data3 => {
    console.log('耶,完成了1,数据是' + data3);
  })
}

async function f2() {
  let data1 = await readFileP(file1)
  console.log('耶,完成了1,数据是' + data1);
  let data2 = await readFileP(file2)
  console.log('耶,完成了2,数据是' + data1 + data2);
  let data3 = await readFileP(file3)
  console.log('耶,完成了2,数据是' + data1 + data2 + data3);

}
f()

async/await的实现原理?

  • async最终返回一个Promise

  • 写一个递归函数,让generator自动执行

    1. generator与async的差别是async里面的await 函数可以自动串行执行
  1. generator的value和done状态是迭代器协议的返回值

    • value是yield的返回值, done是false时,继续执行迭代器, done为true时,resolve结果
function asyncFn(genFn) {
  const g = genFn()
  return new Promise((resolve, reject) => {
    function autoRunNext(g, nextVal) {
      const { value, done } = g.next(nextVal)
      // 迭代器未执行完
      if (!done) {
        value.then((res) => {
          autoRunNext(g, res)
        })
      } else {
        // 迭代器执行完
        resolve(value)
      }
    }
    // 第一次执行autoRunNext是用来启动遍历器,不用传参数
    autoRunNext(g)
  })
}

// 测试

const getData = (i) => new Promise((resolve) => setTimeout(() => resolve(`data${i}`), 500))

function* testG() {
  const data1 = yield getData(1)
  console.log('data1: ', data1)
  const data2 = yield getData(2)
  console.log('data2: ', data2)
  return 'success'
}

asyncFn(testG).then((res) => {
  console.log(res)
})