Promise 为 js 的异步流程控制处理迈出了一大步。
但我一直没用好错误处理。
抛出 error
生成一个 Promise 实例有两种方式,一种是 new Promise
还有一种是直接 Promise.resolve
或 Promise.reject
。
但在 Promise 中抛出错误只有 throw
和 reject
之分。
1 | // 创建实例时拒绝 |
reject
和 throw
在 Promise 中其实没有区别,都统一被 catch
捕获。
但一些文章说 throw
会在调试的时候被 “全部异常” 断点捕获,而 reject
不会。
我测试后发现,new Promise
的时候 reject
也会被断下,但 Promise.reject
不会。
链中的错误
如果一个 Promise 链很长,错误会找到后续链中最近的一个 catch
1 | Promise.resolve(1) |
在这个链中 3,4 步骤会被跳过,但 catch 过后的 then 依然可以正常执行。
这是很多新手会忽略的一个问题。
未知的异常
第三方封装或者自己疏忽导致在 new Promise
之前就报错,这就尴尬了。
所以就有了 Promise.try
的概念,虽然目前还在 stage 1
阶段,不过社区早早的就各种版本的实现了。
假设我们有个第三方模块叫做 readfile 异步读取一个文件。
1 | readfile('1.txt') |
如果 readfile 在创建 Promise 之前就报错,我们无法 catch 到错误,甚至会影响整个进程导致异常而退出。
借助 Promise.try
来和谐这个操作。
1 | Promise.try(() => readfile('1.txt')) |
这样就完全在我们的掌握之中了。
这里提供一个 sindresorhus 大神实现的 Promise.try
ponyfill 模块 p-try。
Promise.all
在异步并发的时候,Promise.all
是我们最常用的方式。
但如果 all 数组中有一个 Promise 实例异常了,会导致全部结果被丢弃。
1 | const tasks = [ |
这个例子中,我们只能 catch 错误 3,其他结果直接被丢弃了。
虽然其他异步任务都执行了,但依然全部丢弃。
如果这是个 ajax 例子,5个 ajax 任务依然会全部执行,但只要一个错误,就全部丢弃。
其实我们会需要剩下的4个结果,错误的结果我们会提示用户或者重新请求。
所以我们要改造下。
1 | const tasks = [ |
就这么简单,捕获到错误后直接返回,而不是 throw/reject
。
但如果每个异步对象都需要手动 catch 其实也很麻烦,所以可以借助 map 处理。
1 | const tasks = [ |
小结
这里分享了我使用 Promise 时候碰到的一些 错误处理 方面的经验。
其实也都是在各个大神的模块源码或博客中偷学来的。
键整理分享下,如果后续有新技巧,也会继续更新。