【问题标题】:Promise constructor with reject call vs throwing error带有拒绝调用与抛出错误的 Promise 构造函数
【发布时间】:2015-04-26 12:22:17
【问题描述】:

在以下代码中:

var p1 = new Promise(function (resolve, reject) {
    throw 'test1';
});

var p2 = new Promise(function (resolve, reject) {
    reject('test2');
});

p1.catch(function (err) {
    console.log(err); // test1
});

p2.catch(function (err) {
    console.log(err); // test2
});

使用Promise api 中的reject(在p2)和使用throw 引发错误(在p1)之间有什么区别吗?

一模一样?

如果相同,为什么我们需要reject 回调呢?

【问题讨论】:

  • 想想new Promise(_, reject) { setTimeout(reject, 1000); })throw 无法做到这一点。

标签: javascript promise


【解决方案1】:

一个非常有趣的观察是,如果您使用throw,它将首先由reject 处理程序处理,如果没有拒绝处理程序,则由error 处理程序处理。

带有拒绝处理程序块

var allowed = false;
var p1 = new Promise(
function(resolve, reject) {
  if (allowed)
    resolve('Success');
  else
//         reject('Not allowed');
    throw new Error('I threw an error')
})

p1.then(function(fulfilled) {
console.log('Inside resolve handler, resolved value: ' + fulfilled);
}, function(rejected) {
console.log('Inside reject handler, rejected value: ' + rejected);
}).catch(function(error) {
console.log('Inside error handler, error value: ' + error);
})

没有拒绝处理程序块

var allowed = false;
var p1 = new Promise(
function(resolve, reject) {
  if (allowed)
    resolve('Success');
  else
//         reject('Not allowed');
    throw new Error('I threw an error')
})

p1.then(function(fulfilled) {
console.log('Inside resolve handler, resolved value: ' + fulfilled);
}).catch(function(error) {
console.log('Inside error handler, error value: ' + error);
})

此外,catch 块将能够捕获resolve 处理程序中抛出的任何错误。

var allowed = true;
var p1 = new Promise(
function(resolve, reject) {
  if (allowed)
    resolve('Success');
  else
//         reject('Not allowed');
    throw new Error('I threw an error')
})

p1.then(function(fulfilled) {
console.log('Inside resolve handler, resolved value: ' + fulfilled);
throw new Error('Error created inside resolve handler block');
}).catch(function(error) {
console.log('Inside error handler, error value: ' + error);
})

看起来最好使用throw,除非您在运行一些异步任务时不能这样做,否则您必须将reject 回调传递给异步函数。但是有一个解决方法,那就是承诺你的异步功能。更多关于https://stackoverflow.com/a/33446005

【讨论】:

    【解决方案2】:

    我知道这有点晚了,但我真的认为这些答案中的任何一个都不能完全回答我发现这个问题时的问题,这是一个更完整的例子。

    var p1 = new Promise(function (resolve, reject) {
        throw 'test 1.1'; //This actually happens
        console.log('test 1.1.1'); //This never happens
        reject('test 1.2'); //This never happens because throwing an error already rejected the promise
        console.log('test 1.3'); //This never happens
    });
    
    var p2 = new Promise(function (resolve, reject) {
        reject('test 2.1'); //This actually happens
        console.log('test 2.1.1'); //This happens BEFORE the Promise is rejected because reject() is a callback
        throw 'test 2.2'; //This error is caught and ignored by the Promise
        console.log('test 2.3'); //This never happens
    });
    
    var p3 = new Promise(function (resolve, reject) {
        setTimeout(function() { reject('test 3.1');}, 1000); //This never happens because throwing an error already rejected the promise
        throw('test 3.2'); //This actually happens
        console.log('test 3.3'); //This never happens
    });
    
    var p4 = new Promise(function (resolve, reject) {
        throw('test 4.1'); //This actually happens
        setTimeout(function() { reject('test 4.2');}, 1000); //This never happens because throwing an error already rejected the promise
        console.log('test 4.3'); //This never happens
    });
    
    var p5 = new Promise(function (resolve, reject) {
        setTimeout(function() { throw('test 5.1');}, 1000); //This throws an Uncaught Error Exception
        reject('test 5.2'); //This actually happens
        console.log('test 5.3'); //This happens BEFORE the Promise is rejected because reject() is a callback
    });
    
    var p6 = new Promise(function (resolve, reject) {
        reject('test 6.1'); //This actually happens
        setTimeout(function() { throw('test 6.2');}, 1000); //This throws an Uncaught Error Exception
        console.log('test 6.3'); //This happens BEFORE the Promise is rejected because reject() is a callback
    });
    
    
    p1.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test1
    });
    
    p2.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test2
    });
    
    p3.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test3
    });
    
    p4.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test4
    });
    
    p5.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test5
    });
    
    p6.then(function (resolve) {
        console.log(resolve, "resolved")
    }, function (reject) {
        console.log(reject, "rejected")
    }).catch(function (err) {
        console.log(err, "caught"); // test6
    });

    【讨论】:

      【解决方案3】:

      使用Promise api 中的reject(在p2)和使用throw 引发错误(在p1)之间有什么区别吗?

      是的,你 cannot 异步使用 throw,而 reject 是一个回调。例如,一些超时:

      new Promise(_, reject) {
          setTimeout(reject, 1000);
      });
      

      一模一样?

      不,至少在您的语句后面有其他代码时不会。 throw 立即完成解析器功能,同时调用 reject 继续正常执行 - 在将承诺“标记”为被拒绝之后。

      此外,如果您throw 错误对象,引擎可能会提供不同的异常调试信息。

      对于您的具体示例,您是对的,p1p2 与外部无法区分。

      【讨论】:

      • '如果你抛出错误对象,引擎可能会提供不同的异常调试信息。' - 我不认为这部分适用于 throw 与 reject 或者你的意思是一般情况?
      • @BenjaminGruenbaum 嗯,我以为我遇到了一个引擎,它的堆栈跟踪区分了创建和抛出错误。不记得是哪个了:-/
      • 如果你找到了,请务必提交一份错误报告 - 错误会在创建时创建堆栈跟踪,而不是在抛出时创建 :)
      • 在像Angular2这样的框架中,使用throw也会触发Angular ErrorHandler,而使用reject可以避免这种情况。
      【解决方案4】:

      不,没有,两者完全相同。唯一的区别以及我们需要 reject 的原因是当您需要异步拒绝时 - 例如,如果您正在转换基于回调的 API,它可能需要发出异步错误信号。

      var p = new Promise(function(resolve, reject){
          someCallbackApi(function(err, data){
              if(err) reject(err); // CAN'T THROW HERE, non promise context, async.
              else resolve(data);
          });
      });
      

      【讨论】:

      • 可能是因为您说“完全相同”,然后继续解释不同之处...?
      • @PeterWhitfield 据我所知,这仍然没问题 - 它们在 OP 的用例中完全相同,但是考虑到 在另一种使用模式中的其他输入它们不同。
      猜你喜欢
      • 1970-01-01
      • 2013-03-22
      • 1970-01-01
      • 2018-04-14
      • 2018-01-27
      • 1970-01-01
      • 2016-04-12
      • 1970-01-01
      • 2013-01-01
      相关资源
      最近更新 更多