将Javascript ES6承诺支持'完成' API?

时间:2014-10-31 04:32:32

标签: javascript promise ecmascript-6 es6-promise

例如

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;
    }
}

3 个答案:

答案 0 :(得分:47)

没有

在规范的未来版本中,.done不仅可能不受支持 - 它是不必要的。引用Mariusz与之相关的主题:

Domenic:

  

它仍然容易出错:如果你滑倒并且不遵守规则,你可能会永远沉默错误。

Mark Miller(开创了承诺的概念):

  

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

Yehuda Kats在RSVP的错误处理程序:

  

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

     

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

     

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

而且,根据规范之前的实际回购,Domenic说:

  

完成的工作将通过将未处理的拒绝跟踪功能集成到开发工具中来完成。大多数TC39,根据我的理解,以及我自己,认为足以使规范完整。< / p>


规范委员会不仅忽视.done,他们认为这是不必要的,容易出错。新的现代承诺库自动检测未处理的拒绝 - 两个例子是当Promises和Bluebird承诺开创这个想法时。

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

不相信我?打开Firefox并使用其原生承诺:

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

简单地说 - firefox使用垃圾收集挂钩来确定promises处于未处理状态并触发一个全局错误处理程序,默认情况下在屏幕上写入。

现在,问题是本机承诺还不是很有用 - 因为在IE中它们不存在而且在Chrome中未处理的拒绝检测尚未实现 - 但它即将到来且它将会存在。同时你可以使用像Bluebird这样的ES6兼容库,它会为你做这种拒绝跟踪。

如果你想完成填充(我强烈建议反对) - torazaburo的polyfill有一些缺点。它在promise原型上声明了一个可枚举的属性,通常这不是规范的设计方式 - 你需要子类 promises来扩展它们而不是猴子修补它们 - 遗憾的是没有实现当前支持这个

简而言之:

  • 在使用之前等待本机承诺稳定 - 同时您可以使用实现规范的库,如Bluebird。当它稳定下来时.done根本不会成为问题。
  • 利用模式检测错误 - 例如,在此处查看the disposer pattern
  • 在可用时使用开发人员工具,长堆栈跟踪和异步调试是很重要的。另请注意,如果需要有意义的堆栈跟踪,则不应抛出字符串。

祝你好运,编码愉快。

答案 1 :(得分:19)

不,AFAIK done不属于规范。为了模仿它的行为,你应该在promises链的范围之外的下一个tick上抛出异常:

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;
};

然后在下一个tick上调用处理程序:

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

答案 2 :(得分:3)

TC39的当前声明是,这个问题可以并且应该在使用开发人员工具的浏览器引擎中本地解决。这就是他们在原生API中提供done的原因。

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

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