Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancellable forEach #3977

Merged
merged 6 commits into from
Aug 3, 2018
Merged

Cancellable forEach #3977

merged 6 commits into from
Aug 3, 2018

Conversation

benlesh
Copy link
Member

@benlesh benlesh commented Jul 30, 2018

  • Adds cancellation semantic to forEach, by allowing a Subscription to be passed as a sort of cancellation token.
  • Adds documentation
  • Fixes some bad tests
  • Deprecation deprecates passing PromiseCtor to forEach. (That can be configured globally, and I'd like to remove it for v7)

Behavior Described

You can read the docs in the PR for this, but basically:

If you call forEach with a second argument that is a Subscription, that subscription will act as a cancellation token, such that if you unsubscribe from it, the forEach's returned promise will reject with an Error with the message Observable forEach unsubscribed. This is done, so there's a way to gracefully handle cancellations in async-await code, and I think provides a better alternative to takeUntil in those use cases.

@benlesh benlesh changed the title Cancellable for each Cancellable forEach Jul 30, 2018
forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise<void> {
forEach(next: (value: T) => void, promiseCtorOrSubscription?: PromiseConstructorLike|Subscription): Promise<void> {
let promiseCtor: PromiseConstructorLike;
let subs = isSubscription(promiseCtorOrSubscription) ? promiseCtorOrSubscription : undefined;
promiseCtor = getPromiseCtor(promiseCtor);
Copy link
Collaborator

@cartant cartant Jul 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't promiseCtorOrSubscription be passed here? In the refactoring, the promiseCtor that's passed to getPromiseCtor will always be undefined.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated.

@coveralls
Copy link

coveralls commented Jul 30, 2018

Coverage Status

Coverage increased (+0.005%) to 96.951% when pulling 4707a92 on benlesh:cancellable-forEach into 5a6c90f on ReactiveX:master.

// Since the consuming code can no longer interfere with the synchronous
// producer, the remaining results are nexted.
expect(results).to.deep.equal([1, 2, 3, 4, expected]);
expect(results).to.deep.equal([1, 2, 3, expected]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to this test's expectation suggests that the behaviour of the previous, non-cancellable implementation was actually a bug - as next was called despite an error being effected. The new behaviour makes more sense to me, as an error effected from next sees the observer unsubscribe.

Could the comment be removed? It's no longer applicable, as the remaining results are not nexted.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I'll make sure to add the fix to the changelog too.

Copy link
Collaborator

@cartant cartant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

IIRC, the consensus in the meeting was that it would be good to throw an instance of a specific error class when an explicit unsubscribe is performed.

Is that something that would be done in this PR or in a separate PR?

@benlesh
Copy link
Member Author

benlesh commented Aug 2, 2018

@cartant ... I'm having it reject with an ObjectUnsubscribedError... it seemed fitting and it already existed.

Copy link
Collaborator

@cartant cartant left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@felixfbecker
Copy link
Contributor

What I am worried about is forward compatibility with whatever cancellation primitive makes it into the spec. We already have AbortSignal in the DOM spec, and there is ongoing work to get a primitive into ES, which I would love love love to see supported seamlessly in rxjs.

@benlesh
Copy link
Member Author

benlesh commented Aug 3, 2018

What I am worried about is forward compatibility with whatever cancellation primitive makes it into the spec

I'm not worried about this in the slightest. We can deprecate and pivot if we need to, but honestly, I have yet to see a proposal or cancellation type that isn't compatible with what we have here. The committees are slow, and unlikely to pass anything related to any of this. CancellationToken failed once. Observable is stuck at stage 1 forever.

@benlesh benlesh merged commit 3d90810 into ReactiveX:master Aug 3, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Sep 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants