Skip to content

zone.js promises returning Unhandled Promise Rejection error even with catch blocks #51328

@ioancris

Description

@ioancris

Which @angular/* package(s) are the source of the bug?

zone.js

Is this a regression?

No

Description

I've hit the same issue as described in #31680 and both this and that ticket have same root cause detailed bellow. Since I cannot reopened old issue I've created a new one. That older ticket was closed with a fix that was for a separate issue and the fix was not relevant or helped in anyway.

Repro

Following simplified code will emit unhandled promise rejection even tho there is a catch handler. This happens only when zone.js is loaded. Without zone.js, all majors browsers (Chrome, Safari, Firefox) don't report anything in this case.

onunhandledrejection = (e) => { console.log("Unhandled promise rejection", e) }

(async function() {
    return Promise.reject(1);
})().catch(e => {});
// OR
(async function() {
    await Promise.reject(2);
})().catch(e => {});

Using native Promise.reject will not emit unhandled rejection.

Root cause:

zone.js schedules the reporting of unhandled rejections on the microtask queue as soon as the promise is rejected. In the case of Promise.reject it schedules as part of the reject call.
JS async function calls .then on the returned inner promise (Promise.reject) on the next microtask tick. This is defined in the Ecmascript spec.
zone.js will end up running the code to report unhandled rejections before JS async function has a chance to call then and as such zone.js ends emiting unhandled rejection.

All major browsers check if unhandled rejections exists at the end of the microtask checkpoint (i.e. after draining the queue) and if needed will schedule on normal queue a task (or what you call a macro task) to process&report unhandled rejections. This allows sufficient time for the async infrastructure to hook to the returned promise. In Chrome and Safari it even allows sufficient time to add catch handler in a callback run by setTimeout of delay 0.

Chrome:
https://github.com/chromium/chromium/blob/89e00b6b959f5efb4bd7eec468973badb3093a3b/third_party/blink/renderer/platform/scheduler/common/event_loop.cc#L108
https://github.com/chromium/chromium/blob/89e00b6b959f5efb4bd7eec468973badb3093a3b/third_party/blink/renderer/bindings/core/v8/rejected_promises.cc#L248-L252
Firefox:
https://github.com/mozilla/gecko-dev/blob/77dd6aa3810610949a5ff925e24de2f8c11377fd/xpcom/base/CycleCollectedJSContext.cpp#L473-L480
Safari:
https://github.com/WebKit/WebKit/blob/75a3eb3a5fe3dce1867d880faf785137963b234a/Source/WebCore/dom/Microtasks.cpp#L107
https://github.com/WebKit/WebKit/blob/75a3eb3a5fe3dce1867d880faf785137963b234a/Source/WebCore/dom/RejectedPromiseTracker.cpp#L142-L144

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

Unhandled Promise rejection: 1 ; Zone: <root> ; Task: null ; Value: 1 undefined
Unhandled Promise rejection: 2 ; Zone: <root> ; Task: null ; Value: 2 undefined
Unhandled promise rejection PromiseRejectionEvent {isTrusted: true,  reason: 1, type: 'unhandledrejection' ...}
Uncaught (in promise) 1
Unhandled promise rejection PromiseRejectionEvent {isTrusted: true,  reason: 2, type: 'unhandledrejection' ...}
Uncaught (in promise) 2

Please provide the environment you discovered this bug in (run ng version)

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: zonesIssues related to zone.jsbug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions