The standards proposals are leaning towards using AbortSignal and AbortController (ala fetch cancellation) for cancellation with Observables. I generally find this favorable for a few reasons and I think I see a migration path for us.
Proposal
- Observable
subscribe accepts Observer and an optional AbortSignal.
- Observable
forEach accepts an optional AbortSignal.
- The subscriber function for
new Observable(subscriber) gives both a safe Observer and an AbortSignal, but is expected to return void.
- The
Observer passed to subscribe will have an optional abort handler that will get the AbortError if the observable is aborted.
- If an abort is triggered on a subscription that happened with
forEach, the AbortError would go down the rejection path just like it does with fetch. Since the returned promise is a guarantee to a future where the producer either errors or completes, it makes sense to send the AbortError down the rejection path in this case.
Advantages
- Synchronous observables cannot firehose and lock up an app, the cancellation semantic exists prioer to subscription.
- If multiple data producers are set up in a subscriber function (in the Observable constructor), you can add them to the abort signal one at a time, guaranteeing they can be torn down if a subsequent step fails. This wasn't possible before without some serious hackery.
- Observables all the way down. Since
AbortSignal is an EventTarget, and EventTarget may soon have an on method that returns an observable. you could use abortSignal.on('abort') to compose teardown if you so choose.
- We will now have the ability to be notified, uniquely, of cancellation (without having to do trickery to figure out if it was really an error or a completion)
Migration path
We'll start using AbortSignal internally throughout the library.
- Add an overload to
subscribe that accepts an Observer and an AbortSignal. If AbortSignal is passed, it does not return a Subscription.
- Make
Subscription into an AbortController
- Add the ability to create an
AbortSignal.
- Alias
unsubscribe as abort.
- Have the subscriber function passed to the
Observable constructor receive an AbortSignal.
- Any function/Subscription returned from the subscriber function (from the constructor), will automatically be added to the
AbortSignal.
- Remove the observer
start method I just added. :) lol It's no longer necessary.
- Add the observer
abort method that accepts AbortError.
- Ensure that aborts arriving at the tail-end of a
forEach get sent as rejections of type AbortError to the returned Promise.
- BONUS: Have
toPromise accept an abort signal
- Add deprecation messages for usages that don't align with the spec.
- Long term, I'd like to phase out the 3 handlers method:
subscribe(fn, fn , fn), because object literals are more readable anyhow, an forEach can be used for the general case of just needing next. Think subscribe(null, (err) => doAThing(err)) vs subscribe({ error: doAThing })
NOTE: The current TC39 proposal may not reflect this yet
cc/ @jhusain @trxcllnt @kwonoj @mattpodwysocki
The standards proposals are leaning towards using AbortSignal and AbortController (ala
fetchcancellation) for cancellation with Observables. I generally find this favorable for a few reasons and I think I see a migration path for us.Proposal
subscribeacceptsObserverand an optionalAbortSignal.forEachaccepts an optionalAbortSignal.new Observable(subscriber)gives both a safeObserverand anAbortSignal, but is expected to return void.Observerpassed to subscribe will have an optionalaborthandler that will get theAbortErrorif the observable is aborted.forEach, theAbortErrorwould go down the rejection path just like it does with fetch. Since the returned promise is a guarantee to a future where the producer either errors or completes, it makes sense to send theAbortErrordown the rejection path in this case.Advantages
AbortSignalis an EventTarget, and EventTarget may soon have anonmethod that returns an observable. you could useabortSignal.on('abort')to compose teardown if you so choose.Migration path
We'll start using
AbortSignalinternally throughout the library.subscribethat accepts anObserverand anAbortSignal. IfAbortSignalis passed, it does not return aSubscription.Subscriptioninto anAbortControllerAbortSignal.unsubscribeasabort.Observableconstructor receive anAbortSignal.AbortSignal.startmethod I just added. :) lol It's no longer necessary.abortmethod that acceptsAbortError.forEachget sent as rejections of typeAbortErrorto the returnedPromise.toPromiseaccept an abort signalsubscribe(fn, fn , fn), because object literals are more readable anyhow, anforEachcan be used for the general case of just needingnext. Thinksubscribe(null, (err) => doAThing(err))vssubscribe({ error: doAThing })NOTE: The current TC39 proposal may not reflect this yet
cc/ @jhusain @trxcllnt @kwonoj @mattpodwysocki