异步操作占据日常业务编码的大部分时间,本文将提出一种优雅的异步操作方案。使得 Promise 再也无需 try-catch,让异步编程分支更流畅,开发体验更佳。而且逼迫开发者必须将异常放到第一位去考虑,避免忘记 try-catch 导致出现大量的 Unhandled Promise Rejection。
/**
* Promise 再也无需 try catch,让异步编程分支更流畅,开发体验更佳。
* 而且逼迫开发者必须将异常放到第一位去考虑,避免忘记 try catch 导致出现大量的 Unhandled Promise rejection
*
* @param promise
* @returns 返回 tuple,失败则第一项是 error,成功第一项为 null,第二项是返回值。
*/
export async function box<T>(
promise: Promise<T>,
): Promise<[Err: (Error & T) | null, Res: T | null]> {
try {
return [undefined, await promise]; // 返回 undefined 而不是 null 是为了支持解构默认值
} catch (error) {
return [error, undefined];
}
}
版本 1
try {
const resp = await fetchList();
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
} catch (error) {
// 处理错误
logger.error(error);
}
版本 1 违反了『对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问题』。
版本 2
仅对请求 try-catch。
let resp: IListResp; // 需要显示申明类型,否则类型将是 any
try {
resp = await fetchList();
} catch (error) {
// 处理错误
logger.error(error);
return; // 错误处理完毕立即退出
}
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
版本 2 有一个缺陷:需要显示申明类型,否则返回值类型将是 any。
版本 3
const [error, resp] = await box(fetchList()); // error 和 resp 自动推断类型
if (error) {
// 处理错误
logger.error(error);
return;
}
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
// do a lot of processing with resp
版本 3 将错误放到第一位,错误处理不可能被忘记而且错误处理更自然,类似 go 语言。