浅析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,这个过程是promisependingreject的改变过程。

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是从pendingreject的状态改变,但是一个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()方法。

posted @ 2017-10-19 19:51  古兰精  阅读(2079)  评论(0编辑  收藏  举报