Skip to content

Commit 16bd691

Browse files
kwonojbenlesh
authored andcommitted
fix(timeout): update behavior of timeout, timeoutWith
1 parent d5120d2 commit 16bd691

File tree

4 files changed

+288
-80
lines changed

4 files changed

+288
-80
lines changed

spec/operators/timeout-spec.js

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,53 @@
1-
/* globals describe, it, expect */
1+
/* globals describe, it, expect, expectObservable, hot, rxTestScheduler */
22
var Rx = require('../../dist/cjs/Rx');
33
var Observable = Rx.Observable;
44

5-
describe('Observable.prototype.timeout', function () {
6-
it('should timeout after a specified delay', function (done) {
7-
Observable.never().timeout(100)
8-
.subscribe(function (x) {
9-
throw 'should not next';
10-
}, function (err) {
11-
expect(err.message).toBe('timeout');
12-
done();
13-
}, function () {
14-
throw 'should not complete';
15-
});
16-
}, 2000);
5+
describe('Observable.prototype.timeout()', function () {
6+
var defaultTimeoutError = new Error('timeout');
177

18-
it('should timeout after a delay and send the passed error', function (done) {
19-
Observable.never().timeout(100, 'hello')
20-
.subscribe(function () {
21-
throw 'should not next';
22-
}, function (err) {
23-
expect(err).toBe('hello');
24-
done();
25-
}, function () {
26-
throw 'should not complete';
27-
})
8+
it('should timeout after a specified timeout period', function () {
9+
var e1 = Observable.never();
10+
var expected = '-----#';
11+
12+
expectObservable(e1.timeout(50, null, rxTestScheduler)).toBe(expected, null, defaultTimeoutError);
13+
});
14+
15+
it('should timeout after specified timeout period and send the passed error', function () {
16+
var e1 = Observable.never();
17+
var expected = '-----#';
18+
var value = 'hello';
19+
20+
expectObservable(e1.timeout(50, value, rxTestScheduler)).toBe(expected, null, value);
21+
});
22+
23+
it('should not timeout if source completes within absolute timeout period', function() {
24+
var e1 = hot('--a--b--c--d--e--|');
25+
var expected = '--a--b--c--d--e--|';
26+
27+
var timeoutValue = new Date(Date.now() + (expected.length + 2) * 10);
28+
29+
expectObservable(e1.timeout(timeoutValue, null, rxTestScheduler)).toBe(expected);
2830
});
2931

32+
it('should not timeout if source emits within timeout period', function() {
33+
var e1 = hot('--a--b--c--d--e--|');
34+
var expected = '--a--b--c--d--e--|';
35+
36+
expectObservable(e1.timeout(50, null, rxTestScheduler)).toBe(expected);
37+
});
3038

31-
it('should timeout at a specified Date', function (done) {
32-
var date = new Date(Date.now() + 100);
39+
it('should timeout after a specified timeout period between emit with default error while source emits', function () {
40+
var e1 = hot('---a---b---c------d---e---|');
41+
var expected = '---a---b---c----#';
3342

34-
Observable.never().timeout(date)
35-
.subscribe(function (x) {
36-
throw 'should not next';
37-
}, function (err) {
38-
expect(err.message).toBe('timeout');
39-
done();
40-
}, function () {
41-
throw 'should not complete';
42-
});
43-
}, 2000);
43+
expectObservable(e1.timeout(50, null, rxTestScheduler)).toBe(expected, {a: 'a', b: 'b', c: 'c'}, defaultTimeoutError);
44+
});
45+
46+
it('should timeout after a specified delay with passed error while source emits', function () {
47+
var value = 'hello';
48+
var e1 = hot('---a---b---c------d---e---|');
49+
var expected = '---a---b---c----#';
50+
51+
expectObservable(e1.timeout(50, value, rxTestScheduler)).toBe(expected, {a: 'a', b: 'b', c: 'c'}, value);
52+
});
4453
});

spec/operators/timeoutWith-spec.js

Lines changed: 128 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,136 @@
1-
/* globals describe, it, expect */
1+
/* globals describe, it, expect, expectObservable, hot, cold, rxTestScheduler */
22
var Rx = require('../../dist/cjs/Rx');
33
var Observable = Rx.Observable;
44

5-
describe('Observable.prototype.timeoutWith', function () {
6-
it('should timeout after a specified delay then subscribe to the passed observable', function (done) {
7-
var expected = [1, 2, 3];
8-
Observable.never().timeoutWith(100, Observable.of(1,2,3))
9-
.subscribe(function (x) {
10-
expect(x).toBe(expected.shift());
11-
}, null, done);
5+
describe('Observable.prototype.timeoutWith()', function () {
6+
it('should timeout after a specified period then subscribe to the passed observable', function () {
7+
var e1 = Observable.never();
8+
var e2 = cold('--x--y--z--|');
9+
var expected = '-------x--y--z--|';
10+
11+
expectObservable(e1.timeoutWith(50, e2, rxTestScheduler)).toBe(expected);
12+
});
13+
14+
it('should timeout at a specified date then subscribe to the passed observable', function (done) {
15+
var expected = ['x', 'y', 'z'];
16+
var e1 = Observable.never();
17+
var e2 = Observable.fromArray(expected);
18+
19+
var res = [];
20+
e1.timeoutWith(new Date(Date.now() + 100), e2)
21+
.subscribe(function (x) {
22+
res.push(x);
23+
}, function(x) {
24+
throw 'should not be called';
25+
}, function() {
26+
expect(res).toEqual(expected);
27+
done();
28+
});
1229
}, 2000);
1330

31+
it('should timeout after a specified period between emit then subscribe to the passed observable when source emits', function () {
32+
var e1 = hot('---a---b------c---|');
33+
var e2 = cold('-x-y-|');
34+
var expected = '---a---b----x-y-|';
35+
36+
expectObservable(e1.timeoutWith(40, e2, rxTestScheduler)).toBe(expected);
37+
});
1438

15-
it('should timeout at a specified date then subscribe to the passed observable', function (done) {
16-
var expected = [1, 2, 3];
17-
var date = new Date(Date.now() + 100);
18-
Observable.never().timeoutWith(date, Observable.of(1,2,3))
19-
.subscribe(function (x) {
20-
expect(x).toBe(expected.shift());
21-
}, null, done);
39+
it('should timeout after a specified period then subscribe to the passed observable when source is empty', function () {
40+
var e1 = hot('-------------|');
41+
var e2 = cold('----x----|');
42+
var expected = '--------------x----|';
43+
44+
expectObservable(e1.timeoutWith(100, e2, rxTestScheduler)).toBe(expected);
45+
});
46+
47+
it('should timeout after a specified period between emit then never completes if other source does not complete', function () {
48+
var e1 = hot('--a--b--------c--d--|');
49+
var e2 = cold('-');
50+
var expected = '--a--b----';
51+
52+
expectObservable(e1.timeoutWith(40, e2, rxTestScheduler)).toBe(expected);
53+
});
54+
55+
it('should timeout after a specified period then subscribe to the passed observable when source raises error after timeout', function () {
56+
var e1 = hot('-------------#');
57+
var e2 = cold('----x----|');
58+
var expected = '--------------x----|';
59+
60+
expectObservable(e1.timeoutWith(100, e2, rxTestScheduler)).toBe(expected);
61+
});
62+
63+
it('should timeout after a specified period between emit then never completes if other source emits but not complete', function () {
64+
var e1 = hot('-------------|');
65+
var e2 = cold('----x----');
66+
var expected = '--------------x----';
67+
68+
expectObservable(e1.timeoutWith(100, e2, rxTestScheduler)).toBe(expected);
69+
});
70+
71+
it('should not timeout if source completes within timeout period', function () {
72+
var e1 = hot('-----|');
73+
var e2 = cold('----x----');
74+
var expected = '-----|';
75+
76+
expectObservable(e1.timeoutWith(100, e2, rxTestScheduler)).toBe(expected);
77+
});
78+
79+
it('should not timeout if source raises error within timeout period', function () {
80+
var e1 = hot('-----#');
81+
var e2 = cold('----x----|');
82+
var expected = '-----#';
83+
84+
expectObservable(e1.timeoutWith(100, e2, rxTestScheduler)).toBe(expected);
85+
});
86+
87+
it('should not timeout if source emits within timeout period', function() {
88+
var e1 = hot('--a--b--c--d--e--|');
89+
var e2 = cold('----x----|');
90+
var expected = '--a--b--c--d--e--|';
91+
92+
expectObservable(e1.timeoutWith(50, e2, rxTestScheduler)).toBe(expected);
93+
});
94+
95+
it('should timeout after specified Date then subscribe to the passed observable', function(done) {
96+
var e1 = Observable.interval(40).take(5);
97+
var e2 = Observable.of(100);
98+
99+
var res = [];
100+
e1.timeoutWith(new Date(Date.now() + 100), e2)
101+
.subscribe(function (x) {
102+
res.push(x);
103+
}, function(x) {
104+
throw 'should not be called';
105+
}, function() {
106+
expect(res).toEqual([0, 1, 100]);
107+
done();
108+
});
22109
}, 2000);
110+
111+
it('should not timeout if source completes within specified Date', function() {
112+
var e1 = hot('--a--b--c--d--e--|');
113+
var e2 = cold('--x--|');
114+
var expected = '--a--b--c--d--e--|';
115+
116+
var timeoutValue = new Date(Date.now() + (expected.length + 2) * 10);
117+
118+
expectObservable(e1.timeoutWith(timeoutValue, e2, rxTestScheduler)).toBe(expected);
119+
});
120+
121+
it('should not timeout if source raises error within specified Date', function() {
122+
var e1 = hot('---a---#');
123+
var e2 = cold('--x--|');
124+
var expected = '---a---#';
125+
126+
expectObservable(e1.timeoutWith(new Date(Date.now() + 100), e2, rxTestScheduler)).toBe(expected);
127+
});
128+
129+
it('should timeout specified Date after specified Date then never completes if other source does not complete', function() {
130+
var e1 = hot('---a---b---c---d---e---|');
131+
var e2 = cold('-')
132+
var expected = '---a---b--';
133+
134+
expectObservable(e1.timeoutWith(new Date(Date.now() + 100), e2, rxTestScheduler)).toBe(expected);
135+
});
23136
});

src/operators/timeout.ts

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,72 @@ import Subscription from '../Subscription';
77
import isDate from '../util/isDate';
88

99
export default function timeout(due: number|Date, errorToSend: any = null, scheduler: Scheduler = immediate) {
10-
let waitFor = isDate(due) ? (+due - Date.now()) : <number>due;
11-
return this.lift(new TimeoutOperator(waitFor, errorToSend, scheduler));
10+
let absoluteTimeout = isDate(due);
11+
let waitFor = absoluteTimeout ? (+due - Date.now()) : <number>due;
12+
13+
return this.lift(new TimeoutOperator(waitFor, absoluteTimeout, errorToSend, scheduler));
1214
}
1315

1416
class TimeoutOperator<T, R> implements Operator<T, R> {
15-
constructor(private waitFor: number, private errorToSend: any, private scheduler: Scheduler) {
17+
constructor(private waitFor: number, private absoluteTimeout:boolean, private errorToSend: any, private scheduler: Scheduler) {
1618
}
1719

1820
call(subscriber: Subscriber<R>) {
19-
return new TimeoutSubscriber(subscriber, this.waitFor, this.errorToSend, this.scheduler);
21+
return new TimeoutSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.errorToSend, this.scheduler);
2022
}
2123
}
2224

2325
class TimeoutSubscriber<T> extends Subscriber<T> {
24-
timeoutSubscription: Subscription<any>;
26+
private index: number = 0;
27+
private _previousIndex: number = 0;
28+
get previousIndex():number {
29+
return this._previousIndex;
30+
}
31+
private _hasCompleted: boolean = false;
32+
get hasCompleted(): boolean {
33+
return this._hasCompleted;
34+
}
2535

26-
constructor(destination: Subscriber<T>, private waitFor: number, private errorToSend: any, private scheduler: Scheduler) {
36+
constructor(destination: Subscriber<T>, private absoluteTimeout:boolean, private waitFor: number, private errorToSend: any, private scheduler: Scheduler) {
2737
super(destination);
28-
let delay = waitFor;
29-
scheduler.schedule(dispatchTimeout, delay, { subscriber: this });
38+
this.scheduleTimeout();
39+
}
40+
41+
private static dispatchTimeout(state: any): void{
42+
const source = state.subscriber;
43+
const currentIndex = state.index;
44+
45+
if (!source.completed && source.previousIndex === currentIndex) {
46+
source.notifyTimeout();
47+
}
48+
}
49+
50+
private scheduleTimeout():void {
51+
let currentIndex = this.index;
52+
this.scheduler.schedule(TimeoutSubscriber.dispatchTimeout, this.waitFor, { subscriber: this, index: currentIndex });
53+
this.index++;
54+
this._previousIndex = currentIndex;
3055
}
3156

32-
sendTimeoutError() {
57+
_next(value: T) {
58+
this.destination.next(value);
59+
60+
if (!this.absoluteTimeout) {
61+
this.scheduleTimeout();
62+
}
63+
}
64+
65+
_error(err) {
66+
this.destination.error(err);
67+
this._hasCompleted = true;
68+
}
69+
70+
_complete() {
71+
this.destination.complete();
72+
this._hasCompleted = true;
73+
}
74+
75+
notifyTimeout() {
3376
this.error(this.errorToSend || new Error('timeout'));
3477
}
35-
}
36-
37-
function dispatchTimeout<T>(state: { subscriber: TimeoutSubscriber<T> }) {
38-
const subscriber = state.subscriber;
39-
subscriber.sendTimeoutError();
4078
}

0 commit comments

Comments
 (0)