From d12c1d47ddd1b694c8c22800f256c8906a95d76d Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Thu, 7 Jan 2021 14:07:16 -0800 Subject: [PATCH] Chore: Refactor rejection tracking and add comments --- src/rejection-tracker.js | 65 ++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/src/rejection-tracker.js b/src/rejection-tracker.js index 8f5289b..3ece67a 100644 --- a/src/rejection-tracker.js +++ b/src/rejection-tracker.js @@ -9,17 +9,15 @@ import { PledgeSymbol } from "./pledge-symbol"; // https://html.spec.whatwg.org/multipage/webappapis.html#promiserejectionevent //----------------------------------------------------------------------------- -const canceled = Symbol("canceled"); - class PledgeRejectionEvent { constructor(pledge, reason) { this.pledge = pledge; this.reason = reason; - this[canceled] = false; + this.returnValue = true; } preventDefault() { - this[canceled] = true; + this.returnValue = false; } } @@ -28,23 +26,37 @@ export class RejectionTracker { constructor(logger = console) { /** - * This set keeps track of promises that were rejected but were not - * handled. All promises in this set will trigger an unhandledrejection - * event if they are still there during the monitoring phase of - * the rejection tracking. + * This set keeps track of pledges that were rejected but were not + * handled. All pledges in this set will trigger onUnhandledRejection + * if they are still there during the monitoring phase of the + * rejection tracking. * @type Set * @property aboutToBeNotified */ this.aboutToBeNotified = new Set(); /** - * This set keeps track of rejected promises that have been handled - * and will trigger a rejectionhandled event. - * @type Set + * This set keeps track of pledges that were unhandled, triggered + * onUnhandledRejection, and then had a rejection handler added. These + * pledges will trigger onRejectionHandled. + * @type WeakSet * @property outstandingRejections */ this.outstandingRejections = new WeakSet(); + + /** + * A logger used to output unhandled pledge rejection notices. This + * can be overwritten for easier testing but otherwise is console. + * @type console + * @property logger + */ this.logger = logger; + + /** + * Tracks the interval identifier for monitoring pledges. + * @type int + * @property timeoutId + */ this.timeoutId = 0; } @@ -74,16 +86,22 @@ export class RejectionTracker { const event = new PledgeRejectionEvent(p, p[PledgeSymbol.result]); p.constructor.onUnhandledRejection(event); - const notHandled = !event[canceled]; - + /* + * In the browser, "not handled" is a term that means both that + * the promise hasn't been handled and that the event handler + * did not cancel the event. + */ + const notHandled = event.returnValue; + + /* + * The onUnhandledRejection handler might have added a rejection + * handler, so we need to double-check here. + */ if (p[PledgeSymbol.isHandled] === false) { this.outstandingRejections.add(p); } if (notHandled) { - - // what to do here? - this.logger.error(`Pledge rejection was not caught: ${ p[PledgeSymbol.result] }`); } } @@ -97,20 +115,35 @@ export class RejectionTracker { track(pledge, operation) { + // the pledge has no rejection handler so we might need to log it if (operation === "reject") { this.aboutToBeNotified.add(pledge); } + // the pledge was unhandled but now is handled if (operation === "handle") { + + /* + * If the pledge was going to trigger onUnhandledRejection, remove + * it from the list so that doesn't happen. + */ if (this.aboutToBeNotified.has(pledge)) { this.aboutToBeNotified.delete(pledge); return; } + /* + * If the pledge isn't already flagged to trigger + * onRejectionHandled, then there's nothing else we need to do. + */ if (!this.outstandingRejections.has(pledge)) { return; } + /* + * If we made it here, the pledge was already flagged to trigger + * onRejectionHandled, so remove it and trigger the event. + */ this.outstandingRejections.delete(pledge); setTimeout(() => {