简体   繁体   English

在promise链中吞噬了异常

[英]Exception gets swallowed in promise chain

I noticed something very strange happening when an exception is thrown inside a chain of promises in Parse for React Native. 当Parse for React Native中的一系列promise中抛出异常时,我注意到发生了一些非常奇怪的事情。 The promise chain never resolves, and never rejects, and the exception is never thrown. 承诺链永远不会解决,永远不会拒绝,并且永远不会抛出异常。 It just disappears silently. 它只是默默地消失。

Here's sample code to recreate the problem: 这是重新创建问题的示例代码:

// Replacing this with Promise.resolve() prints the error.
// Removing this stage prints the error.
Parse.Promise.as()
  // Removing this stage causes a red screen error.
  .then(function() {
    // Replacing this with Parse.Promise.as() causes a red screen error.
    return Promise.resolve();
  })
  .then(function () {
    throw new Error("There was a failure");
  })
  .then(function () { console.log("Success")}, function (err) { console.log(err) });

As you can see from the comments, it only seems to happen in this particular sequence of events. 正如您从评论中看到的那样,它似乎只发生在这个特定的事件序列中。 Removing a stage, or swapping a Parse promise for a native JS promise, causes things to behave again. 删除一个阶段,或者交换一个原生JS承诺的Parse承诺,会导致事情再次发生。 (In my actual code, the "Promise.resolve()" stage is actually a call into a native iOS method that returns a promise.) (在我的实际代码中,“Promise.resolve()”阶段实际上是对返回promise的本机iOS方法的调用。)

I'm aware that Parse promises don't behave exactly like A+ compliant promises (cf. https://stackoverflow.com/a/31223217/2397068 ). 我知道Parse承诺的行为与A +兼容的承诺完全不同 (参见https://stackoverflow.com/a/31223217/2397068 )。 Indeed, calling Parse.Promise.enableAPlusCompliant() before this section of code causes the exception to be caught and printed. 实际上,在此代码段之前调用Parse.Promise.enableAPlusCompliant()会导致捕获并打印异常。 But I thought that Parse promises and native JS promises could be used together safely. 但我认为Parse promises和原生JS承诺可以安全地一起使用。

Why is this exception disappearing silently? 为什么这个异常会默默地消失?

Thank you. 谢谢。

For your consideration in addition to technical reasons provided in your quoted answer: 除了引用答案中提供的技术原因外,供您考虑:

Compliant promises 符合承诺

ES6/A+ compliant Promise instances share: 符合ES6 / A +标准的Promise实例共享:

  1. When a promise is settled, its settled state and value are immutable. 当承诺得到解决时,其结算的状态和价值是不可变的。
  2. A promise cannot be fulfilled with a promise. 诺言不能兑现承诺。
  3. A then registered 'fulfill' listener is never called with a promise (or other thenable) object as argument. 然后,使用promise(或其他可能的)对象作为参数调用随后注册的“fulfill”侦听器。
  4. Listeners registered by then are executed asynchronously in their own thread after the code which caused them to be executed has run to completion. 通过注册的监听器then是造成他们要执行已经运行完代码后在自己的线程异步执行。
  5. Irrespective of whether a listener was registered for call back whan a promise becomes settled ( 'fulfilled' or 'rejected'), 无论听众是否注册了回叫,承诺都会得到解决(“履行”或“被拒绝”),

    • the return value of a listener is used to fulfill resolve the promise returned by its then registration 侦听器的返回值是用来实现 解决其返回的承诺, then登记
    • a value thrown (using throw ) by a listener is used to reject the promise returned by then registration, and 监听器throw的值(使用throw )用于拒绝then注册返回的promise,和
  6. A promise resolved with a Promise instance synchronizes itself with the eventual settled state and value of the promise provided as argument. 使用Promise实例解析的promise将自身与作为参数提供的最终已解决状态和promise的值同步。 (In the ES6 standard this is described as "locked in"). (在ES6标准中,这被描述为“锁定”)。

  7. A promise resolved with a thenable object which is not a Promise instance will skip synch as required: if at first resolved with a thenable which "fulfills" itself with a thenable, the Promise promise will re-synchronize itself with the most recent 'thenable' provided. 使用不是 Promise实例的thenable对象解决的promise将根据需要跳过同步:如果最初使用thenable来“解析”自己的thenable,则Promise promise将与最近的'thenable'重新同步提供。 Skipping to a new thenable to synchronize with cannot occur with A+ promises because they never call a "fulfilled" listener with a thenable object. 使用A + promises时,跳过一个新的可以与之同步并不会发生,因为它们永远不会使用一个可对象的对象调用“已完成”的侦听器。

Non Compliant promises 不合规的承诺

Potential characteristics of non compliant promise like objects include 不符合承诺的潜在特征包括对象

  • allowing the settled state of a promise to be changed, 允许改变承诺的既定状态,
  • calling a then 'fulfilled' listener with a promise argument, 使用promise参数调用当时'履行'的监听器,
  • calling a then listener, either for 'fulfilled' or 'rejected' states, synchronously from code which resolves or rejects a promise, 通过解析或拒绝承诺的代码同步调用一个侦听器,用于“已完成”或“已拒绝”状态,
  • not rejecting a then returned promise after a listener registered in the then call throws an exception. 在then调用中注册的侦听器抛出异常后,不拒绝then返回的promise。

Interoperability 互通性

Promise.resolve can be used to settle its returned promise with a static value, perhaps mainly used in testing. Promise.resolve可用于通过静态值来解决其返回的promise,可能主要用于测试。 Its chief purpose, however, is to quarantine side effects of non compliant promises. 然而,它的主要目的是隔离不合规承诺的副作用。 A promise returned by Promise.resolve( thenable) will exhibit all of behaviours 1-7 above, and none of the non compliant behaviours. Promise.resolve( thenable)返回的承诺将展示上述1-7的所有行为,并且不会出现任何不符合行为的行为。

IMHO I would suggest only using non A+ promise objects in the environment and in accordance with documentation for the library which created them. 恕我直言我建议只在环境中使用非A + promise对象,并根据创建它们的库的文档。 A non compliant thenable can be used to to resolve an A+ promise directly (which is what Promise.resolve does), but for full predictability of behaviour it should be wrapped in a Promise object using Promise.resolve( thenable) before any other use. 非兼容的thenable可用于直接解析A + promise(这是Promise.resolve所做的),但是对于行为的完全可预测性,它应该在任何其他使用之前使用Promise.resolve( thenable)包装在Promise对象中。

Note I tried to test Parse promises for A+ compliance but it does not seem to provide a constructor. 注意我试图测试Parse承诺的A +合规性,但它似乎没有提供构造函数。 This makes claims of "almost" or "fully" A+ compliance difficult to quantify. 这使得“几乎”或“完全”A +合规性的主张难以量化。 Thanks to zangw for pointing out the possibility of returning a rejected promise form a listener as an alternative to throwing an exception. 感谢zangw指出可能会将拒绝的promise作为一个侦听器返回,作为抛出异常的替代方法。

Why is this exception disappearing? 为什么这个例外消失了?

Parse does by default not catch exceptions, and promises do swallow them. Parse默认情况下不会捕获异常,并且promises会吞下它们。

Parse is not 100% Promises/A+ compatible, but nonetheless it does try to assimilate thenables that are returned from then callbacks. 解析是不是100%的承诺/ A +兼容,但尽管如此它试图吸收那些从返回thenables then回调。 And it does neither catch exceptions nor executes its own callbacks asynchronously. 并且它既不捕获异常也不异步执行自己的回调。

What you are doing can be reproduced without then using 你在做什么,不能够被复制then使用

var p1 = new Parse.Promise();
var p2 = new Parse.Promise();

// p2 should eventually settle and call these:
p2._resolvedCallbacks = [function (res) { console.log("Success") }];
p2._rejectedCallbacks = [function (err) { console.log(err) }];

function handler() {
    throw new Error("There was a failure");
}
// this is what the second `then` call sets up (much simplified):
p1._resolvedCallbacks = [function(result) {
    p2.resolve(handler(result)); // throws - oops
}];

// the native promise:
var p = Promise.resolve();
// what happens when a callback result is assimilated:
if (isThenable(p))
    p.then(function(result) {
        p1.resolve(result);
    });

The problem is that p1.resolve is synchronous, and executes the callbacks on p1 immediately - which in turn does throw. 问题是p1.resolve是同步的,并立即在p1上执行回调 - 这反过来会抛出。 By throwing before p2.resolve can be called, p2 will stay forever pending. 通过在p2.resolve调用之前p2.resolvep2将永远保持未决状态。 The exceptions bubbles up and becomes the completion of p1.resolve() - which now throws in a callback to a native then method. 例外冒泡并变成完成p1.resolve() -现在抛出在回调于天然then方法。 The native promise implementation catches the exception and rejects the promise returned by then with it, which is however ignored everywhere. 本机promise实现捕获异常并拒绝then返回的promise,但是无处不在。

silently? 默默?

If your "native" promise implementation supports unhandled rejection warnings, you should be able to see the exception hanging around in the rejected promise. 如果您的“本机”承诺实现支持未处理的拒绝警告,您应该能够在被拒绝的承诺中看到异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM