diff --git a/src/bun.js/bindings/webcore/AbortSignal.cpp b/src/bun.js/bindings/webcore/AbortSignal.cpp index 0c7a3a6de6..7e964f742c 100644 --- a/src/bun.js/bindings/webcore/AbortSignal.cpp +++ b/src/bun.js/bindings/webcore/AbortSignal.cpp @@ -156,6 +156,11 @@ void AbortSignal::signalAbort(JSC::JSValue reason) // 5. Fire an event named abort at signal. dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No)); + + // 6. For each dependent signal of signal, call signal's signalAbort method with reason. + auto dependentSignals = std::exchange(m_dependentSignals, {}); + for (auto& signal : dependentSignals) + signal.signalAbort(reason); } void AbortSignal::cleanNativeBindings(void* ref) diff --git a/test/js/web/abort/abort.test.ts b/test/js/web/abort/abort.test.ts index 46c1f12f3e..bf266d21b7 100644 --- a/test/js/web/abort/abort.test.ts +++ b/test/js/web/abort/abort.test.ts @@ -39,4 +39,35 @@ describe("AbortSignal", () => { expect(exitCode).toBe(0); }); + + test("AbortSignal.any() should fire abort event", async () => { + async function testAny(signalToAbort: number) { + const { promise, resolve } = Promise.withResolvers(); + + const a = new AbortController(); + const b = new AbortController(); + // @ts-ignore + const signal = AbortSignal.any([a.signal, b.signal]); + const timeout = setTimeout(() => { + resolve(false); + }, 100); + + signal.addEventListener("abort", () => { + clearTimeout(timeout); + resolve(true); + }); + + if (signalToAbort) { + b.abort(); + } else { + a.abort(); + } + + expect(await promise).toBe(true); + expect(signal.aborted).toBe(true); + } + + await testAny(0); + await testAny(1); + }); });