async & await

May 10, 2017 by Sylvenas

关于异步的实现,上面的一节中的需求:有接口1,接口2,接口3,要求按照顺序输出接口1,接口2,接口3的返回值 如果用async函数来实现,可以这样写:

async function getData() {
  await loadData1()
  await loadData2()
  await loadData3()
}
getData()

async就像是Generator中的*await就相当于Generator中的yield,同样的await只能在使用了async的函数内使用。

Generator + Promise

看一个简单的基于Promise实现的ajax的例子:

function foo(x, y){
    return request(`http://some.url?x=${x}&y=${y}`)
}

foo(1,2)
    .then(x => console.log(x), err => console.log(err))

那么如果我们想用Generator包装一下这个ajax请求呢,我们可以使用yield把foo函数返回的promise返回出来,然后等待这个promise的决议,然后把决议之后的结果在通过yield的双向通道传递给Generator函数,听起来非常的绕口,我们看一下代码:

function foo(x, y){
    return request(`http://some.url?x=${x}&y=${y}`)
}

function *main(){
    try {
        var text = yield foo(1, 2);
        console.log(text);
    }catch(err){
        console.log(err);
    }
}

const it = main();
const promise = it.next().value;

promise.then(x => it.next(x), err => it.throw(err))

实际上这并没有那么痛苦,对吧,但是如果有一个方法能够帮助我们实现重复(即循环)的执行迭代控制,每次生成一个promise,待其决议之后再继续,那该多好啊,这是时候,async/await就会登场了

实际上async/await组合就是Generator + Promise的语法糖

async函数的返回值

async function doSth() {
    const hello = 'hello';
}
console.log(doSth()); // => Promise {<resolved>:undefined}

async 自动把函数转换为 Promise,返回的是一个Promise对象

async的实战

还是那个需求稍微修改一下:接口2的请求依赖接口1的返回值,接口3依赖接口2的返回值

// 一个函数p11,返回一个axios,Promise对象
function p11() {
    return axios('https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/promise1')
        .then(({
        data
    }) => {
        if (data.data.name) {
            return data.data.name
        }
    })
}
// 一个函数p22,返回一个axios,Promise对象
function p22() {
  return axios('https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/promise2')
    .then(({
      data
    }) => {
      if (data.data.name) {
        return data.data.name
      }
    })
}
// 一个函数p33,返回一个axios,Promise对象
function p33() {
  return axios('https://easy-mock.com/mock/5b0525349ae34e7a89352191/example/mock')
    .then(({
      data
    }) => {
      if (data.success) {
        return data.data.projects
      }
    })
}

基于Promise的实现:

p11()
  .then(res1 => {
    console.log(res1);
    return p22()
  })
  .then(res2 => {
    console.log(res2);
    return p33()
  }).then(res3 => {
    console.log(res3);
    return res3
  })

基于async的处理

async function asyncFun() {
  const RES1 = await p11();
  console.log(RES1);
  const RES2 = await p22();
  console.log(RES2);
  const RES3 = await p33();
  console.log(RES3);
  return RES3;
}
asyncFun()

await的存在 使异步变成了同步 在处理异步的时候,await后面要根的是一个返回Promise的函数,如果不是就需要使用Promise包装一下 就像上面的p11,p22,p33,函数都是返回一个axios(axios返回一个Promise对象)

async函数处理报错

看到了,async中没有then、catch,怎么处理报错信息呢? 天无绝人之路,使用try catch:

async function asyncFUn() {
  const RES1 = await p11();
  console.log(RES1);
  const RES2 = await p22();
  console.log(RES2);
  const RES3 = await p33();
  console.log(RES3);
  return RES3;
}
try {
  asyncFUn()
} catch (error) {
  console.log(error);
}

总结

我们说起异步,之前首先想到的是回调函数,但是回调函数存在各种问题

ES6中出现了Promise,解决了回调函数问题,Promise中我们又介绍了几个常用的API Promise.all()、Promise.race()、Promise.finally()、Promise.then()、Promise.catch()

我们后来又介绍了Generator,通过Generator引出了async await async就是Generator的语法糖,简化了Generator的使用方法 async无法取代Promise,async的使用依赖于Promise

有人说async await是处理异步的终极方案,这个说法有些极端

处理异步的方法我们介绍很多,没有最好的,只有最合适的,会的多了,选择也就多了,代码质量自然就会高