可以看到yield的权重非常低,仅高于return,所以从字面上看,这个执行的结果很符合我们想要的。
而await关键字的权重要高很多,甚至高于最普通的四则运算,所以必然也是高于三元运算符的。
也就是说await版本的实际执行是这样子的:
(await true) ? Promise.resolve(123) : Promise.resolve(233) // Promise<123>那么我们想要获取预期的结果,就需要添加()来告知解释器我们想要的执行顺序了:
await (true ? Promise.resolve(123) : Promise.resolve(233)) // 123 一定不要漏写 await 关键字这个其实算不上升级时的坑,在使用co时也会遇到,但是这是一个很严重,而且很容易出现的问题。
如果有一个异步的操作用来返回一个布尔值,告诉我们他是否为管理员,我们可能会写这样的代码:
async function isAdmin (id) { if (id === 123) return true return false } if (await isAdmin(1)) { // 管理员的操作 } else { // 普通用户的操作 }因为这种写法接近同步代码,所以遗漏关键字是很有可能出现的:
if (isAdmin(1)) { // 管理员的操作 } else { // 普通用户的操作 }因为async函数的调用会返回一个Promise实例,得益于我强大的弱类型脚本语言,Promise实例是一个Object,那么就不为空,也就是说会转换为true,那么所有调用的情况都会进入if块。
那么解决这样的问题,有一个比较稳妥的方式,强制判断类型,而不是简单的使用if else,使用类似(a === 1)、(a === true)这样的操作。eslint、ts 之类的都很难解决这个问题
一些建议 何时应该用 async ,何时应该直接用 Promise首先,async函数的执行返回值就是一个Promise,所以可以简单地理解为async是一个基于Promise的包装:
function fetchData () { return Promise().resolve(123) } // ==> async function fetchData () { return 123 }所以可以认为说await后边是一个Promise的实例。
而针对一些非Promise实例则没有什么影响,直接返回数据。
在针对一些老旧的callback函数,当前版本的Node已经提供了官方的转换工具,用来将符合Error-first callback规则的异步操作转换为Promise实例:
而一些没有遵守这样规则的,或者我们要自定义一些行为的,那么我们会尝试手动实现这样的封装。
在这种情况下一般会采用直接使用Promise,因为这样我们可以很方便的控制何时应该reject,何时应该resolve。
但是如果遇到了在回调执行的过程中需要发起其他异步请求,难道就因为这个Promise导致我们在内部也要使用.then来处理么?
function getList () { return new Promise((resolve, reject) => { oldMethod((err, data) => { fetch(data.url).then(res => res.json()).then(data => { resolve(data) }) }) }) } await getList()但上边的代码也太丑了,所以关于上述问题,肯定是有更清晰的写法的,不要限制自己的思维。
async也是一个普通函数,完全可以放在任何函数执行的地方。
所以关于上述的逻辑可以进行这样的修改:
function getList () { return new Promise((resolve, reject) => { oldMethod(async (err, data) => { const res = await fetch(data.url) const data = await res.json() resolve(data) }) }) } await getList()这完全是一个可行的方案,对于oldMethod来说,我按照约定调用了传入的回调函数,而对于async匿名函数来说,也正确的执行了自己的逻辑,并在其内部触发了外层的resolve,实现了完整的流程。
代码变得清晰很多,逻辑没有任何修改。
合理的减少 await 关键字await只能在async函数中使用,await后边可以跟一个Promise实例,这个是大家都知道的。
但是同样的,有些await其实并没有存在的必要。
首先有一个我面试时候经常会问的题目:
Promise.resolve(Promise.resolve(123)).then(console.log) // ?最终输出的结果是什么。
这就要说到resolve的执行方式了,如果传入的是一个Promise实例,亦或者是一个thenable对象(简单的理解为支持.then((resolve, reject) => {})调用的对象),那么resolve实际返回的结果是内部执行的结果。
也就是说上述示例代码直接输出123,哪怕再多嵌套几层都是一样的结果。
通过上边所说的,不知大家是否理解了 合理的减少 await 关键字 这句话的意思。
结合着前边提到的在async函数中返回数据是一个类似Promise.resolve/Promise.reject的过程。
而await就是类似监听then的动作。
所以像类似这样的代码完全可以避免:
const imgList = [] async function getImage (url) { const res = await fetch(url) return await res.blob() } await Promise.all(imgList.map(async url => await getImage(url))) // ==> async function getImage (url) { const res = fetch(url) return res.blob() } await Promise.all(imgList.map(url => getImage(url)))上下两种方案效果完全相同。
Express 与 koa 的升级首先,Express是通过调用response.send来完成请求返回数据的。
所以直接使用async关键字替换原有的普通回调函数即可。