promise中的错误捕获

上篇文章留下的一个没有解决的问题:在promise中的错误为什么在外层函数捕获不到?

来看下面一个栗子:

    var o = {};

    function A(){
        try{
            o.show();
        }catch(err){

        }
    }

    function B(){
        try{
            A();
            console.log("你看不见我")
        }catch(err){
            console.log(err)
        }
    }

    B();//输出结果:"你看不见我"

上面的栗子说明 try/catch 在捕获到错误后阻止了错误向上传递。你只能手动抛出错误让上层函数捕获。

    var o = {};

    function A(){
        try{
            o.show();
        }catch(err){
            throw err
        }
    }

    function B(){
        try{
            A();
            console.log("你看不见我")
        }catch(err){
            console.log(err)
        }
    }

    B();//输出结果:"[TypeError: Object #<Object> has no method 'show']"

再看看文章开头提到的问题 在promise中的错误为什么在外层函数捕获不到?

###真相只有一个

promise模块核心代码core.js:

    ...
    /*直接跳至关键处*/
    var cb = state ? deferred.onFulfilled : deferred.onRejected
      if (cb === null) {
        (state ? deferred.resolve : deferred.reject)(value)
        return
      }
      var ret
      /* #A */
      try {
        ret = cb(value)
      }
      catch (e) {
        deferred.reject(e)
        return
      }
      /* #B */
      deferred.resolve(ret)
    ...

注意 #A#B 之间的代码。没错就是它!!!这里 catch 错误后并没有将其抛出,而是传递给了下级 reject 并结束当前函数。

好了,现在菊势基本明了了。问题根源便是 Promise 始终劫持着错误对象没有抛出,导致外层函数捕获不到错误。

那么再往下看 Promise.catch 方法又是如何捕获错误的?

es6-extensions.js:

    Promise.prototype['catch'] = function (onRejected) {
        return this.then(null, onRejected);
    }

原来 Promise.catch 不过是 Promise.then(null,reject) 的缩写版。

其实 Promise 作者是建议在所有 Promise 对象后加上 Promise.done 方法。Promise.catch 方法只是为了与ES6标准规范保持一致而添加的。

done.js:

    'use strict';

    var Promise = require('./core.js')
    var asap = require('asap')

    module.exports = Promise
    Promise.prototype.done = function (onFulfilled, onRejected) {
        var self = arguments.length ? this.then.apply(this, arguments) : this
        self.then(null, function (err) {
            asap(function () {
                throw err
            })
        })
    }

Promise.done 的作用与 Promise.then 基本一致(注意done并没有 return Promise对象,所以 done 只能放在最后),只是在后面加了一层抛出捕获到的错误。