From 0d01c71951acf2b6bf6e070c74e120e57380fe9a Mon Sep 17 00:00:00 2001 From: Josh Bassett Date: Thu, 28 Dec 2017 12:28:45 +1100 Subject: [PATCH] Remove sampleWith and holdWith functions --- src/signal.js | 57 +++++++++++++++------------------------------ test/signal_test.js | 56 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/signal.js b/src/signal.js index eed3ed5..ab9df60 100644 --- a/src/signal.js +++ b/src/signal.js @@ -1,4 +1,4 @@ -import {always, apply, compose, empty, equal, get, head, id, pair, tail} from 'fkit' +import {always, apply, compose, empty, equal, get, head, pair, tail} from 'fkit' import Subscription from './subscription' /** @@ -537,36 +537,25 @@ export default class Signal { } /** - * Emits the most recent value when there is an event on the sampler signal - * `s`. + * Emits the most recent value from the given signal `s` whenever there is an + * event on the sampler signal. * * @param s A signal. * @returns A new signal. */ sample (s) { - return this.sampleWith(id, s) - } - - /** - * Generalises the `sample` function to sample the most recent value when - * there is an event on the sampler signal `s`. The most recent value, and the - * sampler value are combined using the binary function `f`. - * - * @param f A binary function. - * @param s A signal. - * @returns A new signal. - */ - sampleWith (f, s) { let lastValue return new Signal(emit => { - // Buffer the value. - const next = a => { lastValue = a } + const next = () => { + // Emit the buffered value. + if (lastValue !== undefined) { emit.next(lastValue) } + } const subscription1 = this.subscribe({...emit, next}) - // Emit the buffered value. - const subscription2 = s.subscribe(a => emit.next(f(lastValue, a)), emit.error) + // Store the last value. + const subscription2 = s.subscribe(a => { lastValue = a }, emit.error, emit.complete) // Unsubscribe the sampler. return () => { @@ -577,34 +566,26 @@ export default class Signal { } /** - * Pauses emitting values when the most recent value on the sampler signal `s` - * is truthy. It will resume emitting events after there is a falsey value. + * Pauses emitting values from the given signal `s` if the most recent value + * from the sampler signal is truthy. It will resume emitting events after + * there is a falsey value. * * @param s A signal. * @returns A new signal. */ hold (s) { - return this.holdWith(id, s) - } - - /** - * Generalises the `hold` function to pause emitting values when the predicate - * function `f` is true for the most recent sampler signal value. - * - * @param p A predicate function. - * @param s A signal. - * @returns A new signal. - */ - holdWith (p, s) { let lastValue return new Signal(emit => { - const next = a => { if (!lastValue) { emit.next(a) } } + const next = a => { + // Emit the value if the gate is open. + if (!lastValue) { emit.next(a) } + } - const subscription1 = this.subscribe({...emit, next}) + const subscription1 = s.subscribe({...emit, next}) - // Store the hold value. - const subscription2 = s.subscribe(a => { lastValue = p(a) }, emit.error) + // Store the last value. + const subscription2 = this.subscribe(a => { lastValue = a }, emit.error, emit.complete) // Unsubscribe the sampler. return () => { diff --git a/test/signal_test.js b/test/signal_test.js index dfd5192..8221742 100644 --- a/test/signal_test.js +++ b/test/signal_test.js @@ -602,8 +602,8 @@ describe('Signal', () => { describe('#sample', () => { it('emits the most recent value when there is an event on the sampler signal', () => { - const s = Signal.sequentially(500, range(1, 6)) - const t = Signal.periodic(1000).always(1) + const s = Signal.periodic(1000) + const t = Signal.sequentially(500, range(1, 6)) s.sample(t).subscribe(nextSpy, errorSpy, completeSpy) @@ -613,29 +613,52 @@ describe('Signal', () => { assert.strictEqual(nextSpy.callCount, 3); - [2, 4, 6].forEach((ns, index) => { + [1, 3, 5].forEach((ns, index) => { const call = nextSpy.getCall(index) assert.isTrue(call.calledWithExactly(ns)) }, this) assert.isTrue(completeSpy.calledAfter(nextSpy)) }) + + it('emits an error if either signal emits an error', () => { + let a, b + const s = Signal.fromCallback(callback => { + a = e => { callback(e) } + }) + const t = Signal.fromCallback(callback => { + b = e => { callback(e) } + }) + + s.sample(t).subscribe({error: errorSpy}) + + a('foo') + b('foo') + + assert.isTrue(errorSpy.calledTwice) + }) }) - describe('#sampleWith', () => { + describe('#hold', () => { it('emits the most recent value when there is an event on the sampler signal', () => { - const s = Signal.sequentially(500, range(1, 6)) - const t = Signal.periodic(1000).always(1) + let a + const s = Signal.fromCallback(callback => { + a = a => { callback(null, a) } + }) + const t = Signal.sequentially(500, range(1, 6)) - s.sampleWith(add, t).subscribe(nextSpy, errorSpy, completeSpy) + s.hold(t).subscribe(nextSpy, errorSpy, completeSpy) + a(false) clock.tick(1000) + a(true) clock.tick(1000) + a(false) clock.tick(1000) - assert.strictEqual(nextSpy.callCount, 3); + assert.strictEqual(nextSpy.callCount, 4); - [3, 5, 7].forEach((ns, index) => { + [1, 2, 5, 6].forEach((ns, index) => { const call = nextSpy.getCall(index) assert.isTrue(call.calledWithExactly(ns)) }, this) @@ -652,7 +675,7 @@ describe('Signal', () => { b = e => { callback(e) } }) - s.sampleWith(always(), t).subscribe({error: errorSpy}) + s.hold(t).subscribe({error: errorSpy}) a('foo') b('foo') @@ -660,11 +683,22 @@ describe('Signal', () => { assert.isTrue(errorSpy.calledTwice) }) + it('unmounts the original signal when it is unsubscribed', () => { + const unmount = sinon.spy() + const s = new Signal(() => unmount) + const t = Signal.never() + const a = s.hold(t).subscribe(always()) + + a.unsubscribe() + + assert.isTrue(unmount.calledOnce) + }) + it('unmounts the sampler when it is unsubscribed', () => { const unmount = sinon.spy() const s = Signal.never() const t = new Signal(() => unmount) - const a = s.sampleWith(always(), t).subscribe(always()) + const a = s.hold(t).subscribe(always()) a.unsubscribe()