使用 Async 异步函数提升 Promise 的易用性
在写 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 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) } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论