From 1b23aceb66fdcd0858955a6459537fd794880fc5 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Thu, 6 Oct 2016 12:43:22 -0700 Subject: [PATCH] feat(cache): remove `cache` operator Because of all of the confusion around the `cache` operator, this removes the operator from RxJS head of the 5.0.0 release. This is done for a few reasons\: 1. The name `cache` implies it supports all sorts of caching strategies, when this is pretty limited 2. Behavior around unending and hot observables is hard to reason about 3. Once we have a properly designed `cache` operator we can add it at a later time and it will not be a breaking change --- spec/operators/cache-spec.ts | 222 ----------------------------------- src/Rx.ts | 1 - src/add/operator/cache.ts | 11 -- src/operator/cache.ts | 63 ---------- 4 files changed, 297 deletions(-) delete mode 100644 spec/operators/cache-spec.ts delete mode 100644 src/add/operator/cache.ts delete mode 100644 src/operator/cache.ts diff --git a/spec/operators/cache-spec.ts b/spec/operators/cache-spec.ts deleted file mode 100644 index 828c30a5d7..0000000000 --- a/spec/operators/cache-spec.ts +++ /dev/null @@ -1,222 +0,0 @@ -import * as Rx from '../../dist/cjs/Rx'; -import {expect} from 'chai'; -declare const {hot, cold, time, expectObservable, expectSubscriptions}; - -declare const rxTestScheduler: Rx.TestScheduler; - -/** @test {cache} */ -describe('Observable.prototype.cache', () => { - it('should just work™', () => { - let subs = 0; - const source = Rx.Observable.create(observer => { - subs++; - observer.next(1); - observer.next(2); - observer.next(3); - observer.complete(); - }).cache(); - let results = []; - source.subscribe(x => results.push(x)); - expect(results).to.deep.equal([1, 2, 3]); - expect(subs).to.equal(1); - results = []; - source.subscribe(x => results.push(x)); - expect(results).to.deep.equal([1, 2, 3]); - expect(subs).to.equal(1); - }); - - it('should replay values upon subscription', () => { - const s1 = hot( '----a---b---c---| ').cache(undefined, undefined, rxTestScheduler); - const expected1 = '----a---b---c---| '; - const expected2 = ' (abc|)'; - const sub2 = '------------------| '; - - expectObservable(s1).toBe(expected1); - rxTestScheduler.schedule(() => expectObservable(s1).toBe(expected2), time(sub2)); - }); - - it('should not replay values after error with a hot observable', () => { - const s1 = hot('---^---a---b---c---# ').cache(undefined, undefined, rxTestScheduler); - const expected1 = '----a---b---c---# '; - const expected2 = ' -'; - const t = time( '------------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should be resubscribable after error with a cold observable', () => { - const s1 = cold( '----a---b---c---# ').cache(undefined, undefined, rxTestScheduler); - const expected1 = '----a---b---c---# '; - const expected2 = ' ----a---b---c---#'; - const t = time( '------------------| '); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should replay values and and share', () => { - const s1 = hot('---^---a---b---c------------d--e--f-|').cache(undefined, undefined, rxTestScheduler); - const expected1 = '----a---b---c------------d--e--f-|'; - const expected2 = ' (abc)----d--e--f-|'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should have a bufferCount that limits the replay test 1', () => { - const s1 = hot('---^---a---b---c------------d--e--f-|').cache(1); - const expected1 = '----a---b---c------------d--e--f-|'; - const expected2 = ' c--------d--e--f-|'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should have a bufferCount that limits the replay test 2', () => { - const s1 = hot( '----a---b---c------------d--e--f-|').cache(2); - const expected1 = '----a---b---c------------d--e--f-|'; - const expected2 = ' (bc)-----d--e--f-|'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - rxTestScheduler.schedule(() => expectObservable(s1).toBe(expected2), t); - }); - - it('should accept a windowTime that limits the replay', () => { - const w = time( '----------|'); - const s1 = hot('---^---a---b---c------------d--e--f-|').cache(Number.POSITIVE_INFINITY, w, rxTestScheduler); - const expected1 = '----a---b---c------------d--e--f-|'; - const expected2 = ' (bc)-----d--e--f-|'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should handle empty', () => { - const s1 = cold('|').cache(undefined, undefined, rxTestScheduler); - const expected1 = '|'; - const expected2 = ' |'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should handle throw', () => { - const s1 = cold('#').cache(undefined, undefined, rxTestScheduler); - const expected1 = '#'; - const expected2 = ' #'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should handle never', () => { - const s1 = cold('-').cache(undefined, undefined, rxTestScheduler); - const expected1 = '-'; - const expected2 = ' -'; - const t = time( '----------------|'); - - expectObservable(s1).toBe(expected1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected2); - }, t); - }); - - it('should multicast a completion', () => { - const s1 = hot('--a--^--b------c-----d------e-|').cache(undefined, undefined, rxTestScheduler); - const t1 = time( '| '); - const e1 = '---b------c-----d------e-|'; - const t2 = time( '----------| '); - const e2 = ' (bc)--d------e-|'; - const t3 = time( '----------------| '); - const e3 = ' (bcd)--e-|'; - - const expected = [e1, e2, e3]; - - [t1, t2, t3].forEach((t: any, i: number) => { - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected[i]); - }, t); - }); - }); - - it('should multicast an error', () => { - const s1 = hot('--a--^--b------c-----d------e-#').cache(undefined, undefined, rxTestScheduler); - const t1 = time( '| '); - const e1 = '---b------c-----d------e-#'; - const t2 = time( '----------| '); - const e2 = ' (bc)--d------e-#'; - const t3 = time( '----------------| '); - const e3 = ' (bcd)--e-#'; - - const expected = [e1, e2, e3]; - - [t1, t2, t3].forEach((t: any, i: number) => { - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(expected[i]); - }, t); - }); - }); - - it('should limit replay by both count and a window time, test 2', () => { - const w = time( '-----------|'); - const s1 = hot('--a--^---b---c----d----e------f--g--h--i-------|').cache(4, w, rxTestScheduler); - const e1 = '----b---c----d----e------f--g--h--i-------|'; - const t1 = time( '--------------------|'); - // -----------| - const e2 = ' (de)-f--g--h--i-------|'; // time wins - const t2 = time( '-----------------------------------|'); - const e3 = ' (fghi)-|'; // count wins - // -----------| - - expectObservable(s1).toBe(e1); - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(e2); - }, t1); - - rxTestScheduler.schedule(() => { - expectObservable(s1).toBe(e3); - }, t2); - }); - - it('should be retryable', () => { - const source = cold('--1-2-3-#'); - const subs = ['^ ! ', - ' ^ ! ', - ' ^ !']; - const expected = '--1-2-3---1-2-3---1-2-3-#'; - - const result = source.cache(undefined, undefined, rxTestScheduler).retry(2); - - expectObservable(result).toBe(expected); - expectSubscriptions(source.subscriptions).toBe(subs); - }); -}); \ No newline at end of file diff --git a/src/Rx.ts b/src/Rx.ts index 2f4ca8f8bd..5f52c96d28 100644 --- a/src/Rx.ts +++ b/src/Rx.ts @@ -44,7 +44,6 @@ import './add/operator/bufferCount'; import './add/operator/bufferTime'; import './add/operator/bufferToggle'; import './add/operator/bufferWhen'; -import './add/operator/cache'; import './add/operator/catch'; import './add/operator/combineAll'; import './add/operator/combineLatest'; diff --git a/src/add/operator/cache.ts b/src/add/operator/cache.ts deleted file mode 100644 index 848112060c..0000000000 --- a/src/add/operator/cache.ts +++ /dev/null @@ -1,11 +0,0 @@ - -import { Observable } from '../../Observable'; -import { cache, CacheSignature } from '../../operator/cache'; - -Observable.prototype.cache = cache; - -declare module '../../Observable' { - interface Observable { - cache: CacheSignature; - } -} \ No newline at end of file diff --git a/src/operator/cache.ts b/src/operator/cache.ts deleted file mode 100644 index 7aee840a23..0000000000 --- a/src/operator/cache.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Observable } from '../Observable'; -import { Scheduler } from '../Scheduler'; -import { ReplaySubject } from '../ReplaySubject'; -import { Observer } from '../Observer'; -import { Subscription } from '../Subscription'; - -/** - * @param bufferSize - * @param windowTime - * @param scheduler - * @return {Observable} - * @method cache - * @owner Observable - */ -export function cache(bufferSize: number = Number.POSITIVE_INFINITY, - windowTime: number = Number.POSITIVE_INFINITY, - scheduler?: Scheduler): Observable { - let subject: ReplaySubject; - let source = this; - let refs = 0; - let outerSub: Subscription; - - const getSubject = () => { - subject = new ReplaySubject(bufferSize, windowTime, scheduler); - return subject; - }; - - return new Observable((observer: Observer) => { - if (!subject) { - subject = getSubject(); - outerSub = source.subscribe( - (value: T) => subject.next(value), - (err: any) => { - let s = subject; - subject = null; - s.error(err); - }, - () => subject.complete() - ); - } - - refs++; - - if (!subject) { - subject = getSubject(); - } - let innerSub = subject.subscribe(observer); - - return () => { - refs--; - if (innerSub) { - innerSub.unsubscribe(); - } - if (refs === 0) { - outerSub.unsubscribe(); - } - }; - }); -} - -export interface CacheSignature { - (bufferSize?: number, windowTime?: number, scheduler?: Scheduler): Observable; -}