使用 async / await 让你完全告别回调地狱的烦恼

背景

接口的异步请求方式经历了一系列的变化,由最古老的方式(自己封装 XMLHttpRequest / ActiveXObject);到 jQuery 一招先的年代 ajax 大行其道;再到后面 Promise 的出现后,所有的请求的方式都改成了使用 window.fetch / 类 fetch 的请求方式直接返回一个 Promise 作为请求结果返回,解决了前两方式最容易出现的回调地狱的问题。但使用 Promise.then 的方式还是有一定的局限性,虽然可以使用 Promise.all 来支持迸发请求,但使用起来还不是很爽。直到 ES 7 的 async / await 的出现,

让你完全告别回调地狱的烦恼,写异步像写同步一样爽。

使用 async / await 的异步调用的写法

这里以 Vue 中使用 axios 请求为例

// 前置:axios 接口请求时已做好拦截处理,直接调用 axios 请求时,要么直接返回 data 封装后的正确结果,要么直接抛出异常
getUserName () { // 获得用户名接口
  return axios({
    method: 'post',
    url: '/api/getUserName',
    data: {
      xxx,
	  yyy
    }
  })
}
getMobile () { // 获得到手机号接口
  return axios({
    method: 'post',
    url: '/api/getMobile',
    data: {
      xxx,
	  yyy
    }
  })
}
getDetail (userName, mobile) { // 获得 detail 信息的接口依赖于前面 getUserName 的接口及 getMobile 接口。
  return axios({
    method: 'post',
    url: '/api/getDetail',
    data: {
      userName,
	  mobile
    }
  })
}
async getDetailByUserNameNMobile () { // 结果拼接,【注意:async 里面的所有异步或者同步代码都变成同步一行行执行】
  try {
    const userName = await this.getUserName()
	const getMobile = await this.getMobile()
	const result = await this.getDetail(userName, getMobile)
	console.log('结果是:', result)
  } catch (e) { // 异常处理
    console.log(e)
  }
}

使用 async / await polyfill 新版的 es 6 异步请求方式 window.fetch

// 参考 https://github.com/bailicangdu/vue2-elm/blob/master/src/config/fetch.js
const baseUrl = 'http://localhost:3000'; // 可换成自己的

export default async (url = '', data = {}, type = 'GET', method = 'fetch') => {
  type = type.toUpperCase();
  url = baseUrl + url;

  if (type == 'GET') {
    let dataStr = ''; // 数据拼接字符串
    Object.keys(data).forEach(key => {
      dataStr += key + '=' + data[key] + '&';
    })

    if (dataStr !== '') {
      dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'));
      url = url + '?' + dataStr;
    }
  }

  if (window.fetch && method == 'fetch') {
    let requestConfig = {
      credentials: 'include',
      method: type,
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      mode: "cors",
      cache: "force-cache"
    }

    if (type == 'POST' || type == 'PUT' || type == 'PATCH' || type == 'DELETE') {
      requestConfig = {
        ...requestConfig,
        body: JSON.stringify(data)
      }
    }

    try {
      const response = type == 'GET' ? await fetch(url) : await fetch(url, requestConfig);
      const responseJson = await response.json();
      return responseJson;
    } catch (error) {
      throw new Error(error);
    }
  } else {
    return new Promise((resolve, reject) => {
      let requestObj;
      if (window.XMLHttpRequest) {
        requestObj = new XMLHttpRequest();
      } else {
        requestObj = new ActiveXObject;
      }

      let sendData = '';
      if (type == 'POST' || type == 'PUT' || type == 'PATCH' || type == 'DELETE') {
        sendData = JSON.stringify(data);
      }

      requestObj.open(type, url, true);
      requestObj.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      requestObj.send(sendData);

      requestObj.onreadystatechange = () => {
        if (requestObj.readyState == 4) {
          if (requestObj.status == 200) {
            let obj = requestObj.response
            if (typeof obj !== 'object') {
              obj = JSON.parse(obj);
            }
            resolve(obj)
          } else {
            reject(requestObj)
          }
        }
      }
    })
  }
}