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

fix(timeout): do not timeout if source emits synchronously when subscribed #6865

Merged
merged 5 commits into from Mar 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 11 additions & 2 deletions spec/operators/timeout-spec.ts
@@ -1,8 +1,8 @@
/** @prettier */
import { expect } from 'chai';
import { timeout, mergeMap, take } from 'rxjs/operators';
import { timeout, mergeMap, take, concatWith } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';
import { TimeoutError, of, Observable } from 'rxjs';
import { TimeoutError, of, Observable, NEVER } from 'rxjs';
import { observableMatcher } from '../helpers/observableMatcher';

/** @test {timeout} */
Expand Down Expand Up @@ -691,6 +691,15 @@ describe('timeout operator', () => {
expectSubscriptions(inner.subscriptions).toBe([]);
});
});

it('should not timeout if source emits synchronously when subscribed', () => {
rxTestScheduler.run(({ expectObservable, time }) => {
const source = of('a').pipe(concatWith(NEVER));
const t = time(' ---|');
const expected = 'a---';
expectObservable(source.pipe(timeout({ first: new Date(t) }))).toBe(expected);
});
});
});

it('should stop listening to a synchronous observable when unsubscribed', () => {
Expand Down
4 changes: 3 additions & 1 deletion src/internal/operators/timeout.ts
Expand Up @@ -386,10 +386,12 @@ export function timeout<T, O extends ObservableInput<any>, M>(
);

// Intentionally terse code.
// If we've `seen` a value, that means the "first" clause was met already, if it existed.
// it also means that a timer was already started for "each" (in the next handler above).
// If `first` was provided, and it's a number, then use it.
benlesh marked this conversation as resolved.
Show resolved Hide resolved
// If `first` was provided and it's not a number, it's a Date, and we get the difference between it and "now".
// If `first` was not provided at all, then our first timer will be the value from `each`.
startTimer(first != null ? (typeof first === 'number' ? first : +first - scheduler!.now()) : each!);
!seen && startTimer(first != null ? (typeof first === 'number' ? first : +first - scheduler!.now()) : each!);
});
}

Expand Down