凌晨两点 Sentry 报警说有未捕获异常,而那个函数在每一处调用点都挂了 .catch()。作者看了三遍代码才明白:这个函数不是 async 函数,调用它时函数体会同步从头跑到尾,参数类型不对就当场抛错——此时返回值还没产生,连 promise 都没有被创建,.catch() 挂在了一个不存在的对象上,异常顺着调用栈一路展开。这个 bug 只在跳过 return 直接抛错的那条路径上出现,而那恰恰是没人测过的坏输入路径。文章随后说明为什么最直观的 try/catch 包裹调用点会让错误处理出现两份,以及 Promise.try() 如何把同步抛错统一成 rejected promise。