这其实是一篇两年前实习所写 Gist 的中文说明,英文原档见这里

先看看我写的烂代码

async function checkImage (file, pixels) {
const {width, height} = pixels
return new Promise((resolve, reject) => {
try {
const img = await readImageFromFile(file)

if (img.width !== WIDTH || img.height !== HEIGHT) {
alert(`图标文件必须 ${pixels} 像素`)
} else {
resolve(img)
}
} catch (e) {
reject(e)
}
})
}

这是一个检查图片尺寸的 Promise,主要流程就是使用 readImageFromFile 从文件对象中读取 image 对象,然后根据取出来的宽高来判断是否符合我们设定的要求,以此来确认是 resolve 还是 reject 这个 promise。整个流程还是比较简单的,但是依然还是由于对 Promise 了解的不够深入,导致写出了这样的代码。

由于我们是要封装成 Promise 对象返回,所以在第一时间,我写出了 return new Promise 的代码,结果在构造 Promise 的时候,发现 readImageFromFile 这个方法也是一个将 Promise 作为返回值的 API,也就是说,作为异步的方法,我们要么使用如下:

return Promise((resolve, reject) => {
readImageFromFile(file)
.then(img => {
if (img.width !== WIDTH || img.height !== HEIGHT) {
reject(`图标文件必须 ${pixels} 像素`)
} else {
resolve(img)
}
}).catch(e => reject(e))
})

要么就像原先的代码一样,使用 async-await 避免奇葩的 Promise 嵌套,但这样就需要使用 try-catch 来捕获异常了。

看起来总让人感觉有点啰嗦,而这样的结果就是,我们需要使用这样来调用该方法:

checkImage(...)
.then()

当然,如果你不使用 Promise 的话,则需要传入 onSuccess 和 onFailed 回调来解决问题了。

代码的优化

先讲点理论性的东西:
async 修饰的异步方法本身就会返回一个 Promise 对象,其 return 的值会被作为 onResolve 的回调参数被传出,而 throw 抛出的异常,则会作为 onReject 的参数被传出。
当然,你也可以把它当作普通方法来调用,不过这样的话,你是不能够从其返回值里拿到你想要的值的,因为它被作为 Promise 返回了,唯一的办法就是使用 回调来解决问题。

接下来看看优化后的代码吧:

async function checkImage (file, pixels) {
const {width, height} = pixels

try {
const img = await readImageFromFile(file)

if (img.width !== WIDTH || img.height !== HEIGHT) {
alert(`图标文件必须 ${pixels} 像素`)
} else {
return img
}
} catch (e) {
throw e
alert('读取图片文件失败')
}
}

和最原始代码的不同点就是我们去掉了手动封装的 Promise 对象,直接利用了 async-await 特性解决了问题,用法的话,依然和常规的 Promise 是一致的:

let result = await checkImage(file, pixels)
或者
checkImage(file, pixels).then(result => normal, error => exception)

结语

整体看来,并没有什么难点,核心就是帮助我们解决 Promise 和 async-await 使用上的混淆问题。

donation