浅析Promise.catch()错误捕获机制的理解及try catch错误捕获与往上冒泡的处理
一、Promise.prototype.catch()方法是用于指定发生错误时的回调函数
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
上边代码中,getJSON( )方法返回一个Promise对象,如果该对象状态为resolved,则会调用then()方法指定的回调函数;
(1)如果异步操作抛出错误,状态就会变为rejected,就会调用catch( )方法指定的回调函数,处理这个错误。
(2)另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch( )方法捕获。
关于catch方法MDN的描述是这样的:Internally calls Promise.prototype.then on the object upon which is called, passing the parameters undefined and the onRejected handler received; then returns the value of that call (which is a Promise )
这里说到 :
1、.catch
方法在内部也是调用的Promise.prototype.then
方法中的reject
状态下的方法,也就是calling,obj.catch(onRejected)
内部calls obj.then(undefined, onRejected)
;
2、它返回了一个失败的promise
,以及返回一个参数作为一个失败理由。值得注意的是这里返回的是一个已定型的promise
,这个过程是promise
从pending
到reject
的改变过程。
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
如果 Promise
状态已经变成resolved
,再抛出错误是无效的。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test'); //会被忽略,不会被捕获,等于没有抛出
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
这里很好理解,就是catch
是从pending
到reject
的状态改变,但是一个promise一旦resolve以后,状态就凝固了,无法再发生变化,因此会被忽略。
一般来说,不在then()方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法。
// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});
上边的代码,第二种写法优于第一种,理由是第二种写法可以捕获前面then方法执行中的错误,而第一种不行。因此,建议总是使用catch方法,而不是使用then方法的第二个参数。
二、错误捕获机制理解
1、try catch
捕获错误
先来看看 try catch
语句的错误捕获机制:当多层嵌套 try catch 出现时,内部的错误抛出会被最近一个catch
,直到最外层。当错误被内部的catch
捕获后,就失效了。
那么如何让错误“冒泡”呢?2 种方式:
(1)不使用 catch,让错误往上冒泡(这其实跟 Java 里的错误捕获机制一样的)
(2)在 catch 里往上继续抛出错误,让错误“冒泡”才会被捕获
2、promise.catch 捕获错误
promise
错误捕获和try catch
一样会冒泡到外层。
promise
的错误抛出后会一直传递到最外层,直到被捕获;当catch
捕获错误以后,返回的还是一个promise对象,可以继续链式调用
看这个示例:第1个catch捕获到错误,返回的还是一个promise对象,故可继续链式调用,但是值是undefined,但是第2个catch就不会执行咯
接着看示例:如果.then()
方法后面没有catch
方法,后面紧跟着的链式调用的.then()
不会执行,错误一直“冒泡”到最后 catch
3、promise
如果传递未捕获的错误,浏览器会自动抛错,这也就是为什么需要加上 promise 全局捕获错误unhandledrejection 的原因
没有 catch 方法,或 catch() 啥都不处理,就会抛错
三、Promise.catch 与 try catch 的不同点
与传统的try/catch代码块不同的是,如果没有使用catch()方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应
上边的代码中,someAsyncThing( )函数产生的Promise对象,内部有语法错误,浏览器运行到这一行,会打印出错误提示,但是不会退出进程,终止脚本运行,2s之后还是会输出123。 这就是说,Promise内部的错误不会影响到Promise外部的代码,通俗的说法就是“Promise 会吃掉错误”。
而使用 try catch 则报错后,后面的延时代码也不会执行。
这个脚本放在服务器执行,退出码是0(即表示执行成功)。不过,nodejs 里有一个unhandleRejection事件,专门监听未捕获的reject错误,上边的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。
process.on('unhandledRejection', function (err, p) {
throw err;
});
上边的代码中,unhandleRejection事件的监听函数有两个参数,第一个是错误对象,第二个是报错的Promise实例,它可以用来了解发生错误的环境信息。
注意,Node 有计划在未来废除unhandleRejection事件。如果Promise内部有未捕获的错误,会终止进程,并且进程的退出码不为0.
一般总是建议,Promise对象后面要跟catch方法,这样可以处理Promise内部发生的错误。catch( )方法返回的还是一个Promise对象,因此后面还可以接着调用then()方法。