40

例如

p = new Promise(function (resolve, reject) {
    throw 'err';
});

p.done();

在大多数 promise polyfill 库中,done 会抛出错误,并且当前执行将退出。

但是如果我们使用p.then(),什么都不会发生。错误被承诺吞没了。如果我们使用p.catch,我们将无法退出当前执行。我想实现类似:

try {
    // something
} catch (err) {
    if (check(err)) {
        throw err;
    }
}
4

3 回答 3

47

不。

未来版本的规范不仅.done可能不支持 - 它是不需要的。引用 Mariusz 链接到的线程:

家庭:

它仍然容易出错:如果你犯了错误,甚至一次都不遵守规则,你可能会永远沉默错误。

Mark Miller(率先提出承诺的概念):

请注意,弱引用(希望在 ES7 中)将为我们提供弥合这一差距所需的诊断工具之一。使用weak-refs,如果在没有通知任何处理程序的情况下收集了被拒绝的promise,我们可以安排它生成诊断。承诺实现必须将原因保留在承诺的执行程序(事后 gc 处理程序)中,以便在发现承诺已被拒绝后报告诊断。

关于 RSVP 的错误处理程序的 Yehuda Kats:

我们在 RSVP 中采用的方法是安装一个默认抛出的未处理承诺监视器。

如果您知道将附加异步错误处理程序,则可以通过附加 noop 失败处理程序来选择特定的承诺退出此行为。我们可能会为此加糖(.undone :p)

根据我们的经验,将负担从每个人转移到可能想要附加异步错误处理程序的人是合适的。

而且,从规范之前的实际回购中,Domenic 说:

done 的工作将通过将未处理的拒绝跟踪功能集成到开发工具中来完成。据我所知,大多数 TC39ers 以及我自己都认为这足以使规范完整。


规范委员会不仅忽略了.done,他们认为这是不必要的并且容易出错。新的现代 Promise 库会自动检测未处理的拒绝 - 其中两个示例是开创这一想法的 When Promise 和 Bluebird Promise。

.done是一个工件 - 源于浏览器无法检测到未处理的拒绝。事实是——确定性地检测它们是不可能的,但对于绝大多数情况来说,这是完全可能的。

不相信我?打开 Firefox 并使用它的原生 Promise:

p = new Promise(function (resolve, reject) {
    throw 'err';
});
// Logs as error: Unhandled error: `err`

简单地说 - firefox 使用垃圾收集钩子来确定 promise 是否处于未处理状态,并触发默认在屏幕上写入的全局错误处理程序。

现在,问题是本机 Promise 还不是很实用——因为在 IE 中它们不存在,并且在 Chrome 中未处理的拒绝检测尚未实现——但它即将到来,它会在那里。同时,您可以使用像 Bluebird 这样的 ES6 兼容库,它会为您进行拒绝跟踪。

如果你想完成 polyfill(我强烈建议不要这样做)- torazaburo 的 polyfill 有一些缺点。它在 Promise 原型上声明了一个可枚举的属性,通常这不是规范的设计方式 - 您应该继承Promise 以扩展它们而不是猴子修补它们 - 遗憾的是,目前没有实现支持这一点。

简而言之:

  • 等待原生 Promise 稳定后再使用它们——同时你可以使用像 Bluebird 这样实现规范的库。当它稳定下来时,根本.done就不是问题。
  • 利用模式来检测错误 - 例如在此处查看处置器模式
  • 在可用时使用开发人员工具,长堆栈跟踪和异步调试是一大优势。另请注意,如果您想要有意义的堆栈跟踪,则不应抛出字符串。

祝你好运,编码愉快。

于 2014-10-31T11:23:50.010 回答
19

不,AFAIKdone不是规范的一部分。为了模仿它的行为,你应该在下一个滴答声中抛出异常,在 Promise 链的范围之外:

p.catch(function(e) { 
    setTimeout(function() { throw e; });
});

这本质上是库实现的方式done。请参阅 Q 文档的摘录:

很像,但是...所产生的拒绝原因在事件循环的未来轮次中then作为异常抛出。

实施done自己

如果您想实现done通常理解的近似语义,则类似于:

Promise.prototype.done = function(onFulfilled, onRejected) {
    this
        .then(onFulfilled, onRejected)
        .catch(function(e) {
            setTimeout(function() { throw e; });
        })
    ;
};

设置错误处理程序

如果您想自己处理这些错误,可以设置一个错误处理程序:

Promise.onError = function(e) {
    console.log("The sky is falling", e);
    throw e;
};

然后在下一个滴答声中调用处理程序:

Promise.prototype.done = function(onFulfilled, onRejected) {
    this
        .then(onFulfilled, onRejected)
        .catch(function(e) {
            setTimeout(Promise.onError || function() { throw e; }, 1, e);
        })
    ;
};
于 2014-10-31T04:40:16.000 回答
3

TC39 的当前声明是,这个问题可以并且应该在浏览器引擎中使用开发人员工具本地解决。这就是为什么它们done在本机 API 中提供相反的功能。

这确实是一个有争议的决定,有关该问题的讨论,请参阅以下链接:

https://github.com/domenic/promises-unwrapping/issues/19

http://mozilla.6506.n7.nabble.com/Where-d-Promise-done-go-td281461.html

https://github.com/promises-aplus/promises-spec/issues/43

https://github.com/slightlyoff/Promises/issues/33

于 2014-10-31T09:54:58.010 回答