Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 Async 异步函数提升 Promise 的易用性 #7

Open
bouquetrender opened this issue May 9, 2018 · 0 comments
Open

使用 Async 异步函数提升 Promise 的易用性 #7

bouquetrender opened this issue May 9, 2018 · 0 comments

Comments

@bouquetrender
Copy link
Owner

在写 JS 的时候我目前较常用到的两大语法糖分别是 ES2015 中的 Class 与 ES2017 中的 Async。Async 目前处在 ECMAScript Proposals 的 stage4 阶段(可以在 TC39 Proposals 上查看提案所在阶段,如果对 TC39 草案阶段概念不太熟悉建议阅读这篇文章),是 Generator 的语法糖用于声明一个异步函数,相比 Generator 函数的手动调用 next 或者依靠一些执行器外,Async 函数本身就拥有内部执行器。

通常 Async 函数的 Await 属性后是一个 Promise 对象,并将结果赋值给一个变量或 return。此时 Async 函数执行过程中并不会阻塞代码,而且代码整体观感宛如同步。我想 Async 函数可能就是处理异步的最终解决方案。

利用 Await 函数和 单纯使用 Promise 函数写法区别在于下面这段代码,我写了两个方法使用了 Fetch 请求接口,Fetch 返回的是一个 Promise 对象(实际在项目用 Fetch 请求需要注意许多细节建议阅读这篇文章这篇文章)。

const logFetch = (url) => {
  return fetch(url)
    .then(response => response.text())
    .then(text => { console.log(text) })
    .catch(error => { console.error('fetch failed:', error) })
}

const logFetch = async (url) => {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (error) {
    console.log('fetch failed:', error);
  }
}

利用 try catch 处理 await 错误捕获(关于 Fetch 无论服务器返回什么样的状态码都不会进入到错误捕获里,除了在 Chrome 中出现状态码407、网络有问题和ERR_CONNECTION_RESET这个状态)。

Async 函数明显提高代码可读性,需要注意的是如果 Await 命令后面是除 Promise 对象外的其他类型值则会被转成一个 resolve 的 Promise 对象。不过通常情况下 Await 后都是跟着一个返回 Promise 对象的函数并没有出现在这种值转换的情况。也可以通过手动 reject 返回错误,如下面这段代码:

(async () => {
  await Promise.reject('something error'); 
})().then(val => console.log(val))
    .catch(error => console.log('promise reject', error))

// 通常是在 async 内部进行错误捕获 async 返回的 Promise 对象进行捕获错误处理

(async () => {
  try {
    await Promise.reject('something error'); 
  } catch (error) {
    console.log('promise reject', error)
  }
})();

如果一个 Async 函数中有多个请求且互不相关,建议用使用 Promise.all 来处理,下面这段代码是同时请求两个接口:

(async () => {
  try {
    const getName = () => window.fetch('/api/getName')
    const getValue = () => window.fetch('/api/getValue')
    const [res1, res2] = await Promise.all([getName(), getValue()])
  } catch (error) {
    console.log('fetch failed:', error)
  }
})();

在目前我开发的项目中,使用了 axios 请求库而不是 Fetch 方法。同样 axios 函数请求方法返回的是一个 Promise 对象,进行二次封装暴露方法后,就可以用 Async 函数进行更简易的处理。

import axios from 'axios' 
import querystring from 'querystring' 
const HTTP = axios.create()

function checkStatus (response) {
  if (response && (response.status === 200 || response.status === 304 || response.status === 400)) {
    return response.data
  }
  return { status: -404, msg: '网络异常'}
}
function checkCode (res) {
  if (res.status === -404 || res.code !== 200) {
    // handle data code not 200
  }
  return res
}

export default {
  async post (baseURL, url, data = {}, jsonp = false, qs = true) {
    try {
      let response = await HTTP({
        baseURL,
        timeout: 10000,
        method: 'post',
        url,
        data: qs ? querystring.stringify(data) : data,
        headers: {
          'X-Requested-With': 'XMLHttpRequest',
          'Content-Type': (jsonp ? 'application/json' : 'application/x-www-form-urlencoded; charset=UTF-8'),
        }
      })
      let data = checkStatus(response)
      return checkCode(data)
    } catch (e) {
      console.log('axios req failed:', error)
    }
  }
}

下面这个例子利用 Async 与 Promise 来写图片加载完成后的回调

// <img id="img" src="/img/test.png">

function getImgLoadStatus (img) {
  return new Promise((resolve, reject) => {
    if (img.complete) {
      resolve('done')
    } else {
      img.onload = event => { resolve('done') };
      img.onerror = error => { reject(error) };
    }
  })
}
async function handleImgLoad () {
  try {
    const status = await getImgLoadStatus(document.getElementById('img'))
    if (status === 'done') {
      // do something
    }
  } catch (error) {
    console.log('async failed:', error)
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant