ES2017 —— async await原理

4,453 阅读3分钟

Generator不熟悉的人,可以先看下Generator的文档:developer.mozilla.org/zh-CN/docs/…

前言

在ES2017之前,我们要想把一段异步逻辑编程按照同步代码的方式来编写,需要依赖Generator,比如:

function getJSONFile() {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve({
        name: '大漠传奇',
        age: 23,
      })
    }, 1000)
  })
}

function getHobby() {
  return new Promise(function(resolve, reject){
    setTimeout(function(){
      resolve({
        hobby: ['爬山','旅游','听歌','看电影','看小说','打游戏'],
      })
    }, 1000)
  })
}

function* gen(i) {
  let me = yield getJSONFile();
  console.log('me:', me);
  let hobby = yield getHobby();
  console.log('hobby:', hobby)
}

function co(genFn) {
  let gen = genFn();
  let res = gen.next()
  let run = (gen, res) => {
    if(!res.done){
      if(res.value&&(res.value.__proto__.constructor === Promise)){
        res.value.then((data) => {
          let res2 = gen.next(data)
          run(gen, res2);
        })
      }else{
        let res3 = gen.next(res.value);
        run(gen, res3);
      }
    }
    
  }
  run(gen, res);
}

co(gen)

但是,在ES2017提供了async && await之后,我们上面这一坨代码,就可以用更加优雅的方式来编写了。比如:

// async await
async function likeGen() {
  let me = await getJSONFile();
  console.log('me:', me);
  let hobby = await getHobby();
  console.log('hobby:', hobby);
  return 'end';
}

likeGen().then(res => {
  console.log('res:', res);
})

两相对比,用async+await来编写,最明显的变化就是少了一个co函数,代码量少了一些。其他的代码看起来跟用generator实现相差不大。 那么为什么ES2017提供的async && await来编写同步代码时,代码量少了一些,并且更方便呢?

具体原因我们下面来介绍,我们先看下async && await的功能简介。

async && await功能简介

async

async function 声明用于定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。但是如果你的代码使用了异步函数,它的语法和结构会更像是标准的同步函数。

当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待 Promise 的结果出来,然后恢复async函数的执行并返回解析值(resolved)。

注意, await 关键字仅仅在 async function中有效。如果在 async function函数体外使用 await ,你只会得到一个语法错误(SyntaxError)。

await

await 操作符用于等待一个Promise 对象。它只能在异步函数 async function 中使用。

await 表达式会暂停当前 async function 的执行,等待 Promise 处理完成。若 Promise 正常处理(fulfilled),其回调的resolve函数参数作为 await 表达式的值,继续执行 async function。

若 Promise 处理异常(rejected),await 表达式会把 Promise 的异常原因抛出。

另外,如果 await 操作符后的表达式的值不是一个 Promise,则返回该值本身。

async+await VS generator

ES2017引入async和await目的就是简化用generator来写同步代码时的繁琐,降低用js来写同步代码的复杂程度,降低代码量,让语法更加简单。

你可以把async和await理解为前言中前半部分用generator实现同步代码的上层简易语法糖。也就是浏览器帮我们在js解释器中实现了底层部分,我们用async和await时,js解释器就会理解为generator+co函数(前言中实现的generator自动执行函数)。

这样,我们就不用去关注generator以及如何让generator自动执行的问题了,要想写同步代码,只需要定义一个async函数,在async函数中用await来等待异步代码的执行成功。即简单又方便,代码量还少。