对于 error handling,我的风格碰巧和 Ruby 作者 Matz 一致,虽然是巧合,但是增加了我的信心。
先说 error handling 需要解决哪些问题?
我常用的编程语言有 TypeScript 和 Elixir,Elixir 不用说了,我很喜欢它的 error handling 机制。
我主要分享一下我自己使用 TypeScript 时的 error handling 风格。
try {
try {
throw new Error("oops");
} finally {
console.log("finally");
}
} catch (ex) {
console.error("outer", ex.message);
}
为了避免嵌套 try blocks,我会参考 Elixir/golang/Swift 的方式返回一个数组包含 [isError, data/error]
形如:
//
function doSomething() {
try {
return [false, data]
} catch(error) {
return [true, error]
}
}
const [isError, data] = doSomething()
if(isError) {
// data is error
// your fallback
}
// data is expected data
// next ...
supabase-js 和 zod 也采用了类似的风格,只是细节不太一样,这也增强了我的信心。
// supabase-js
const { data, error } = await supabase
.from('countries')
.select()
// zod .safeParse
stringSchema.safeParse(12);
// => { success: false; error: ZodError }
stringSchema.safeParse("billie");
// => { success: true; data: 'billie' }
为什么我用 Array 而不是 Object 作为返回值?因为我觉得 ES6 以后数组取值也很简单。
// good
async function doSomething(): Promise<Data>
// bad
async function doSomething(): Promise<Data
| RpcException
| ChargeFeeHasNotBeenPaidException
| BadRequestException
| UnusableFacilityException
| NotBusinessDayException>
当然,npm package, public library 除外。
MVC 风格的 web server app 的 exception 最好控制在 controller 层,不要因为个别的 API 异常导致整个 App 崩溃或者重启。
类似 Remix.run 这样的框架甚至可以将 exception 控制在 UI component 级别,不过我不过分强求,只要别让整个 App 崩溃就行。
如果要在动手开发功能前就想明白有多少种 errors 需要处理,对我来说有点难,或许尼古拉 特斯拉那样的大脑可以吧。
实际 coding 时,我的 workflow
refs
备注:
这是我的个人风格,不奢求成为主流,只希望将来有机会合作的朋友能事先了解我的个人风格。