зеркало из https://github.com/mozilla/rhino.git
Fix unhandled promise rejection handler after a `.then`
If we have a promise `p1` that is rejected, and invoke `then` on it, we create a new promise `p2` (as it should be, per the spec) and mark `p1` as handled. However, if we call `.catch` on `p2`, we are _not_ marking `p1` as handled correctly since its status is "pending" and not "rejected". This patch fixes it and adds some tests. Fixes #1461
This commit is contained in:
Родитель
05c033d812
Коммит
15abaadb78
|
@ -306,15 +306,19 @@ public class NativePromise extends ScriptableObject {
|
|||
cx.enqueueMicrotask(() -> fulfillReaction.invoke(cx, scope, result));
|
||||
} else {
|
||||
assert (state == State.REJECTED);
|
||||
if (!handled) {
|
||||
cx.getUnhandledPromiseTracker().promiseHandled(this);
|
||||
}
|
||||
markHandled(cx);
|
||||
cx.enqueueMicrotask(() -> rejectReaction.invoke(cx, scope, result));
|
||||
}
|
||||
handled = true;
|
||||
return capability.promise;
|
||||
}
|
||||
|
||||
private void markHandled(Context cx) {
|
||||
if (!handled) {
|
||||
cx.getUnhandledPromiseTracker().promiseHandled(this);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Promise.prototype.catch
|
||||
private static Object doCatch(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) {
|
||||
Object arg = (args.length > 0 ? args[0] : Undefined.instance);
|
||||
|
@ -446,6 +450,9 @@ public class NativePromise extends ScriptableObject {
|
|||
for (Reaction r : reactions) {
|
||||
cx.enqueueMicrotask(() -> r.invoke(cx, scope, reason));
|
||||
}
|
||||
if (!reactions.isEmpty()) {
|
||||
markHandled(cx);
|
||||
}
|
||||
return Undefined.instance;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,46 @@ public class UnhandledPromiseTest {
|
|||
assertTrue(Context.toBoolean(scope.get("caught", scope)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectionHandledAfterThen() {
|
||||
scope.put("caught", scope, Boolean.FALSE);
|
||||
exec(
|
||||
"new Promise((resolve, reject) => { reject(); })."
|
||||
+ "then(() => {})."
|
||||
+ "catch((e) => { caught = true; });\n");
|
||||
assertTrue(cx.getUnhandledPromiseTracker().enumerate().isEmpty());
|
||||
AtomicBoolean handled = new AtomicBoolean();
|
||||
// We should actually never see anything in the tracker here
|
||||
cx.getUnhandledPromiseTracker()
|
||||
.process(
|
||||
(Object o) -> {
|
||||
assertFalse(handled.get());
|
||||
handled.set(true);
|
||||
});
|
||||
assertFalse(handled.get());
|
||||
assertTrue(Context.toBoolean(scope.get("caught", scope)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thenRejectsButCatchHandles() {
|
||||
scope.put("caught", scope, Boolean.FALSE);
|
||||
exec(
|
||||
"new Promise((resolve, reject) => { resolve(); })."
|
||||
+ "then((e) => { return Promise.reject(); })."
|
||||
+ "catch((e) => { caught = true; });\n");
|
||||
assertTrue(cx.getUnhandledPromiseTracker().enumerate().isEmpty());
|
||||
AtomicBoolean handled = new AtomicBoolean();
|
||||
// We should actually never see anything in the tracker here
|
||||
cx.getUnhandledPromiseTracker()
|
||||
.process(
|
||||
(Object o) -> {
|
||||
assertFalse(handled.get());
|
||||
handled.set(true);
|
||||
});
|
||||
assertFalse(handled.get());
|
||||
assertTrue(Context.toBoolean(scope.get("caught", scope)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectionObject() {
|
||||
exec("new Promise((resolve, reject) => { reject('rejected'); });");
|
||||
|
@ -115,6 +155,22 @@ public class UnhandledPromiseTest {
|
|||
assertTrue(handled.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thenReturnsRejectedPromise() {
|
||||
exec(
|
||||
"new Promise((resolve, reject) => { resolve() }).then(() => { return Promise.reject('rejected'); });");
|
||||
assertEquals(1, cx.getUnhandledPromiseTracker().enumerate().size());
|
||||
AtomicBoolean handled = new AtomicBoolean();
|
||||
cx.getUnhandledPromiseTracker()
|
||||
.process(
|
||||
(Object o) -> {
|
||||
assertFalse(handled.get());
|
||||
handled.set(true);
|
||||
assertEquals("rejected", o);
|
||||
});
|
||||
assertTrue(handled.get());
|
||||
}
|
||||
|
||||
private void exec(String script) {
|
||||
cx.evaluateString(scope, "load('./testsrc/assert.js'); " + script, "test.js", 0, null);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче