如何优雅地实现 async/await 的错误处理
异步编程
该部分可以参考 JavaScript 中的异步编程,其中较为详细地介绍了各种异步编程的实现方案的优缺点,为了方便开发我使用了 async/await 方式去解决异步编程的问题。
错误处理
在上面文章中 async/await 的方案中无法实现 Promise 的错误处理,该问题是在处理项目 网络书签 时遇到的,原始代码如下:
exports.main = async (event) => {
const client = createClient(event.webdavurl, {
username: event.username,
password: event.password,
});
// 获取文件内容
const buff = await client.getFileContents(event.path);
// 将文件内容从二进制转换成字符串
const decoder = new util.TextDecoder();
const xmldata = decoder.decode(buff);
// 解析文件内容
const parser = new xml2js.Parser();
const bookmark = await parser.parseStringPromise(xmldata);
return { xbel: bookmark.xbel };
};
如上可见,async/await 方式中我并没有添加任何错误处理程序,在这两天开始完善代码的时候,尝试过用 Promise.then().catch() 的方法去实现,但不能完美地定位某个错误(增加的那个 if 多丑啊),而且代码量明显增加,代码如下:
exports.main = async (event) => {
const client = createClient(event.webdavurl, {
username: event.username,
password: event.password,
});
const result = await client
.getFileContents(event.path)
.then((buff) => {
// 将文件内容从二进制转换成字符串
const decoder = new util.TextDecoder();
const xmldata = decoder.decode(buff);
// 解析文件内容
const parser = new xml2js.Parser();
return parser.parseStringPromise(xmldata);
})
.then((bookmark) => ({
xbel: bookmark.xbel,
}))
.catch((e) => {
if (e.message === 'Invalid response: 401 Unauthorized') {
return Error('配置信息错误!');
}
return Error('文件解析错误!');
});
if (result instanceof Error) {
return {
error: result.message,
};
}
return result;
};
在有使用 try…catch 代码的想法的时候察觉到了有那么点违和感,所以在网上搜索得知了如下处理方式。
await-to
await-to 的思想很简单,其中借鉴了 Go 的错误处理方法,对 Promise 进行包装。
Go 的错误处理方法如下:
data, err := db.Query("SELECT ...")
if err != nil { return err }
很容易地就想到我们可以采用类似的方式去解决 async/await 的错误。
创建一个包装函数如下:
function to(promise) {
return promise
.then((data) => {
return [null, data];
})
.catch((err) => [err]);
}
上面的代码可以改成:
exports.main = async (event) => {
const client = createClient(
event.webdavurl,
{
username: event.username,
password: event.password,
},
);
// 获取文件内容
const [err1, buff] = await to(client.getFileContents(event.path));
if (err1) return { error: '配置信息错误!' };
// 将文件内容从二进制转换成字符串
const decoder = new util.TextDecoder();
const xmldata = decoder.decode(buff);
// 解析文件内容
const parser = new xml2js.Parser();
const [err2, bookmark] = await to(parser.parseStringPromise(xmldata));
if (err2) return { error: '文件解析错误!' };
return { xbel: bookmark.xbel };
通过这种方式我们的错误处理更加简洁和易读,想要了解更多可以参考 How to write async await without try-catch blocks in Javascript 和 await-to-js。