UNPKG

@virtualstate/app-history

Version:

Native JavaScript [app-history](https://github.com/WICG/app-history) implementation

81 lines 3.82 kB
import { isParallelEvent } from "./parallel-event.js"; import { isSignalEvent, isSignalHandled } from "./signal-event.js"; import { AbortError } from "../app-history-errors.js"; import { EventTargetListenersMatch, EventTargetListenersThis } from "./event-target-options.js"; import { EventTargetListeners } from "./event-target-listeners.js"; export class AsyncEventTarget extends EventTargetListeners { [EventTargetListenersThis]; constructor(thisValue = undefined) { super(); this[EventTargetListenersThis] = thisValue; } async dispatchEvent(event) { const listeners = this[EventTargetListenersMatch]?.(event.type) ?? []; // Don't even dispatch an aborted event if (isSignalEvent(event) && event.signal.aborted) { throw new AbortError(); } const parallel = isParallelEvent(event); const promises = []; for (let index = 0; index < listeners.length; index += 1) { const descriptor = listeners[index]; const promise = (async () => { // Remove the listener before invoking the callback // This ensures that inside of the callback causes no more additional event triggers to this // listener if (descriptor.once) { // by passing the descriptor as the options, we get an internal redirect // that forces an instance level object equals, meaning // we will only remove _this_ descriptor! this.removeEventListener(descriptor.type, descriptor.callback, descriptor); } await descriptor.callback.call(this[EventTargetListenersThis] ?? this, event); })(); if (!parallel) { try { await promise; } catch (error) { if (!isSignalHandled(event, error)) { await Promise.reject(error); } } if (isSignalEvent(event) && event.signal.aborted) { // bye return; } } else { promises.push(promise); } } if (promises.length) { // Allows for all promises to settle finish so we can stay within the event, we then // will utilise Promise.all which will reject with the first rejected promise const results = await Promise.allSettled(promises); const rejected = results.filter((result) => { return result.status === "rejected"; }); if (rejected.length) { let unhandled = rejected; // If the event was aborted, then allow abort errors to occur, and handle these as handled errors // The dispatcher does not care about this because they requested it // // There may be other unhandled errors that are more pressing to the task they are doing. // // The dispatcher can throw an abort error if they need to throw it up the chain if (isSignalEvent(event) && event.signal.aborted) { unhandled = unhandled.filter(result => !isSignalHandled(event, result.reason)); } if (unhandled.length === 1) { await Promise.reject(unhandled[0].reason); throw unhandled[0].reason; // We shouldn't get here } else if (unhandled.length > 1) { throw new AggregateError(unhandled.map(({ reason }) => reason)); } } } } } //# sourceMappingURL=async-event-target.js.map