Skip to content

Commit bb440ad

Browse files
committed
feat(operator): add timeout and timeoutWith
adds two operators timeout and timeoutWith. The former is for sending errors on timeout the latter is for continuing with an Observable on timeout. closes #244
1 parent d86f276 commit bb440ad

File tree

7 files changed

+156
-0
lines changed

7 files changed

+156
-0
lines changed

spec/operators/timeout-spec.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* globals describe, it, expect */
2+
var Rx = require('../../dist/cjs/Rx');
3+
var Observable = Rx.Observable;
4+
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);
17+
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+
})
28+
});
29+
30+
31+
it('should timeout at a specified Date', function (done) {
32+
var date = new Date(Date.now() + 100);
33+
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);
44+
});

spec/operators/timeoutWith-spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* globals describe, it, expect */
2+
var Rx = require('../../dist/cjs/Rx');
3+
var Observable = Rx.Observable;
4+
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);
12+
}, 2000);
13+
14+
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);
22+
}, 2000);
23+
});

src/Observable.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,4 +175,6 @@ export default class Observable<T> {
175175
bufferCount: <T>(bufferSize: number, startBufferEvery: number) => Observable<T[]>;
176176

177177
finally: (ensure: () => void, thisArg?: any) => Observable<T>;
178+
timeout: <T>(due: number|Date, errorToSend?: any, scheduler?: Scheduler) => Observable<T>;
179+
timeoutWith: <T>(due: number|Date, withObservable: Observable<any>, scheduler?: Scheduler) => Observable<T>;
178180
}

src/Rx.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,12 @@ observableProto.retryWhen = retryWhen;
159159
observableProto.repeat = repeat;
160160

161161
import _finally from './operators/finally';
162+
import timeout from './operators/timeout';
163+
import timeoutWith from './operators/timeoutWith';
162164

163165
observableProto.finally = _finally;
166+
observableProto.timeout = timeout;
167+
observableProto.timeoutWith = timeoutWith;
164168

165169
import groupBy from './operators/groupBy';
166170
import window from './operators/window';

src/operators/timeout.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Operator from '../Operator';
2+
import Observer from '../Observer';
3+
import Subscriber from '../Subscriber';
4+
import Scheduler from '../Scheduler';
5+
import Subscription from '../Subscription';
6+
import isDate from '../util/isDate';
7+
8+
export default function timeout(due: number|Date, errorToSend: any = null, scheduler: Scheduler = Scheduler.immediate) {
9+
let waitFor = isDate(due) ? (+due - Date.now()) : <number>due;
10+
return this.lift(new TimeoutOperator(waitFor, errorToSend, scheduler));
11+
}
12+
13+
class TimeoutOperator<T, R> implements Operator<T, R> {
14+
constructor(private waitFor: number, private errorToSend: any, private scheduler: Scheduler) {
15+
}
16+
17+
call(observer: Observer<R>) {
18+
return new TimeoutSubscriber(observer, this.waitFor, this.errorToSend, this.scheduler);
19+
}
20+
}
21+
22+
class TimeoutSubscriber<T> extends Subscriber<T> {
23+
timeoutSubscription: Subscription<any>;
24+
25+
constructor(destination: Observer<T>, private waitFor: number, private errorToSend: any, private scheduler: Scheduler) {
26+
super(destination);
27+
let delay = waitFor;
28+
scheduler.schedule(delay, { subscriber: this }, dispatchTimeout);
29+
}
30+
31+
sendTimeoutError() {
32+
this.error(this.errorToSend || new Error('timeout'));
33+
}
34+
}
35+
36+
function dispatchTimeout<T>(state: { subscriber: TimeoutSubscriber<T> }) {
37+
const subscriber = state.subscriber;
38+
subscriber.sendTimeoutError();
39+
}

src/operators/timeoutWith.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Operator from '../Operator';
2+
import Observer from '../Observer';
3+
import Subscriber from '../Subscriber';
4+
import Scheduler from '../Scheduler';
5+
import Subscription from '../Subscription';
6+
import Observable from '../Observable';
7+
import isDate from '../util/isDate';
8+
9+
export default function timeoutWith(due: number|Date, withObservable: Observable<any>, scheduler: Scheduler = Scheduler.immediate) {
10+
let waitFor = isDate(due) ? (+due - Date.now()) : <number>due;
11+
return this.lift(new TimeoutWithOperator(waitFor, withObservable, scheduler));
12+
}
13+
14+
class TimeoutWithOperator<T, R> implements Operator<T, R> {
15+
constructor(private waitFor: number, private withObservable: Observable<any>, private scheduler: Scheduler) {
16+
}
17+
18+
call(observer: Observer<R>) {
19+
return new TimeoutWithSubscriber(observer, this.waitFor, this.withObservable, this.scheduler);
20+
}
21+
}
22+
23+
class TimeoutWithSubscriber<T> extends Subscriber<T> {
24+
timeoutSubscription: Subscription<any>;
25+
26+
constructor(destination: Observer<T>, private waitFor: number, private withObservable: Observable<any>, private scheduler: Scheduler) {
27+
super(destination);
28+
let delay = waitFor;
29+
scheduler.schedule(delay, { subscriber: this }, dispatchTimeout);
30+
}
31+
32+
handleTimeout() {
33+
const withObservable = this.withObservable;
34+
this.add(withObservable.subscribe(this));
35+
}
36+
}
37+
38+
function dispatchTimeout<T>(state: { subscriber: TimeoutWithSubscriber<T> }) {
39+
const subscriber = state.subscriber;
40+
subscriber.handleTimeout();
41+
}

src/util/isDate.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function isDate(value) {
2+
return value instanceof Date && !isNaN(+value);
3+
}

0 commit comments

Comments
 (0)