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