From 2330c9660b20f2e0cda0c4eeb36bb582b4a85186 Mon Sep 17 00:00:00 2001 From: Daniel Wiehl Date: Tue, 8 Mar 2022 18:03:31 +0100 Subject: [PATCH] fix(timeout): no longer will timeout when receiving the first value synchronously (#6865) * fix(timeout): do not timeout if source emits synchronously when subscribed Closes #6862 * chore: Improve comments * chore: minor test cleanup * chore: minor test cleanup * chore: Fix broken test that I broke! lol Co-authored-by: Ben Lesh --- spec/operators/timeout-spec.ts | 13 +++++++++++-- src/internal/operators/timeout.ts | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/spec/operators/timeout-spec.ts b/spec/operators/timeout-spec.ts index ebedfdac82..9341c56da4 100644 --- a/spec/operators/timeout-spec.ts +++ b/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} */ @@ -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', () => { diff --git a/src/internal/operators/timeout.ts b/src/internal/operators/timeout.ts index 04e05f0ef6..a63febcaff 100644 --- a/src/internal/operators/timeout.ts +++ b/src/internal/operators/timeout.ts @@ -386,10 +386,12 @@ export function timeout, 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. // 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!); }); }