fetch/AbortSignal: honor init.signal + modernize AbortSignal — hop 4 of #196#202
Closed
bkaradzic-microsoft wants to merge 1 commit into
Closed
fetch/AbortSignal: honor init.signal + modernize AbortSignal — hop 4 of #196#202bkaradzic-microsoft wants to merge 1 commit into
bkaradzic-microsoft wants to merge 1 commit into
Conversation
fetch() previously ignored init.signal entirely (it passed arcana::cancellation::none() to both continuations), so AbortController could never cancel a request or reject with AbortError, and the AbortSignal polyfill predated the modern spec. This is hop 4 of BabylonJS#196. AbortSignal: - Add `reason` (read-only) and `throwIfAborted()`. - Add static `AbortSignal.abort(reason?)`. - Make `aborted` read-only (remove the setter). - Abort(reason) now records the reason, defaulting to an AbortError (an Error whose name is "AbortError", as there is no DOMException polyfill). - AbortController.abort(reason) forwards the reason to the signal. fetch: - Honor init.signal via its JS interface (so fetch stays decoupled from the AbortController C++ types): an already-aborted signal rejects synchronously with the signal's reason; otherwise an "abort" listener cancels the transport (UrlRequest::Abort()) and the completion continuation rejects with the signal's reason (an AbortError) instead of a network-error TypeError. The listener is torn down when the request settles, breaking the ownership cycle. Transport cancellation is effective on backends where UrlRequest::Abort() is implemented (Windows today; the curl/NSURLSession backends are a UrlLib follow-up noted in BabylonJS#196). AbortSignal.timeout() is left as a follow-up. Adds JS tests for the modern AbortSignal API and for fetch rejecting with an AbortError both pre-aborted and in-flight (validated on Win32, where the UrlLib backend honors Abort()). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hop 4 of #196 — make
AbortController/init.signalactually work infetch(), and modernize theAbortSignalpolyfill.Problem
fetch()passedarcana::cancellation::none()for both continuations and never looked atinit.signal, so abort never rejected (AbortError) and never cancelled the transport. TheAbortSignalpolyfill also predated the modern spec — noreason, nothrowIfAborted(), a writableaborted, and no staticAbortSignal.abort(). Combined withAbortControllerbeing installed globally in the Playground (BabylonJS/BabylonNative#1708), library feature-detection passed while signals silently no-op'd — making user cancellations indistinguishable from network failures in telemetry.AbortSignal
reason(read-only) andthrowIfAborted().AbortSignal.abort(reason?).abortedis now read-only (setter removed).Abort(reason)records the reason, defaulting to an AbortError — anErrorwhosenameis"AbortError"(there is noDOMExceptionpolyfill, so this matches what web code checks:err.name === "AbortError").AbortController.abort(reason?)forwards the reason to the signal.fetch
init.signalthrough the signal's JS interface (aborted/reason/add/removeEventListener), sofetchstays decoupled from theAbortControllerpolyfill's C++ types.reason, never touching the transport."abort"listener cancels the in-flight transport viaUrlRequest::Abort(), and the completion continuation rejects with the signal'sreason(anAbortError) instead of a network-errorTypeError. The listener is torn down when the request settles (breaking the listener ⟷ state ownership cycle).Validation
Built and ran the UnitTests suite on Win32 / Chakra — all pass (199), including new tests:
AbortSignal.abort()returns an aborted signal whosereasonis anAbortError;throwIfAborted()throws only once aborted;abort(reason)records the reason;abort()defaults to anAbortError.fetchrejects with anAbortErrorboth when the signal is already aborted and when aborted in-flight (the in-flight case cancels the WinRT transport and settles in ~50 ms).Win32's UrlLib backend honors
UrlRequest::Abort(), so the end-to-end abort path is exercised here.Scope / follow-ups
UrlRequest::Abort()on curl/NSURLSession: today it only cancels the Windows backend (the curl/NSURLSession backends never observem_cancellationSource). Making abort interrupt those transports is a UrlLib change — until it lands, in-flight abort on Linux/Apple rejects only once the transport gives up on its own. Tracked in fetch/XHR transport failures and unhandled rejections lose all diagnostic fidelity before reaching the embedding app #196.AbortSignal.timeout()is left as a follow-up.Part of the #196 chain (hops 2 and 3 are #200 and #201).
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com