From 6588884e2ee39b80c7ba2ce3ac5466b0954bc7e6 Mon Sep 17 00:00:00 2001 From: Vitaly Tomilov Date: Mon, 30 Sep 2024 00:29:45 +0100 Subject: [PATCH] Add "emitLast" option support (#32) * add the declaration * add saving the last value * add sendign last event * adding condition * use lastEvent property * refactoring * adding test for lastValue * adding issue reference --- package.json | 2 +- src/event.ts | 38 +++++++++++++++++++++++++++++--------- test/event.spec.ts | 20 ++++++++++++++++++++ typedoc.json | 2 +- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index d408c56..e0f3f5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sub-events", - "version": "1.9.1", + "version": "1.10.0", "description": "Lightweight, strongly-typed events, with monitored subscriptions.", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", diff --git a/src/event.ts b/src/event.ts index 73f722e..55d8366 100644 --- a/src/event.ts +++ b/src/event.ts @@ -215,6 +215,16 @@ export interface ISubscriber extends ISubContext { */ export class SubEvent { + /** + * Last emitted event, if there was any, or `undefined` otherwise. + * + * It is set after all subscribers have received the event, but just before + * optional {@link IEmitOptions.onFinished} callback is invoked. + */ + get lastEvent(): T | undefined { + return this._lastEvent; + } + /** * @hidden */ @@ -226,6 +236,12 @@ export class SubEvent { */ protected _subs: ISubscriber[] = []; + /** + * Last emitted event container. + * @private + */ + private _lastEvent?: T; + /** * Event constructor. * @@ -282,11 +298,11 @@ export class SubEvent { } cb = options && 'thisArg' in options ? cb.bind(options.thisArg) : cb; const cancel = () => { - if (options && typeof options.onCancel === 'function') { + if (typeof options?.onCancel === 'function') { options.onCancel(); } }; - const name = options && options.name; + const name = options?.name; const sub: ISubscriber = {event: this, cb, name, cancel}; if (typeof this.options.onSubscribe === 'function') { const ctx: ISubContext = {event: sub.event, name: sub.name, data: sub.data}; @@ -318,7 +334,7 @@ export class SubEvent { public once(cb: SubFunction, options?: ISubOptions): Subscription { const sub = this.subscribe((data: T) => { sub.cancel(); - return cb.call(options && options.thisArg, data); + return cb.call(options?.thisArg, data); }, options); return sub; } @@ -328,7 +344,7 @@ export class SubEvent { * which is synchronous by default. * * @param data - * Data to be sent, according to the template type. + * Event data to be sent, according to the template type. * * @param options * Event-emitting options. @@ -340,9 +356,9 @@ export class SubEvent { if (typeof (options ?? {}) !== 'object') { throw new TypeError(Stat.errInvalidOptions); } - const schedule: EmitSchedule = (options && options.schedule) ?? EmitSchedule.sync; - const onFinished = options && typeof options.onFinished === 'function' && options.onFinished; - const onError = options && typeof options.onError === 'function' && options.onError; + const schedule: EmitSchedule = options?.schedule ?? EmitSchedule.sync; + const onFinished = typeof options?.onFinished === 'function' && options.onFinished; + const onError = typeof options?.onError === 'function' && options.onError; const start = schedule === EmitSchedule.sync ? Stat.callNow : Stat.callNext; const middle = schedule === EmitSchedule.async ? Stat.callNext : Stat.callNow; start(() => { @@ -360,8 +376,12 @@ export class SubEvent { } else { sub.cb && sub.cb(data); } - if (onFinished && index === r.length - 1) { - onFinished(r.length); // finished sending + if (index === r.length - 1) { + // the end of emission reached; + this._lastEvent = data; // save the last event + if (onFinished) { + onFinished(r.length); // notify + } } })); }); diff --git a/test/event.spec.ts b/test/event.spec.ts index e294eb6..3c27029 100644 --- a/test/event.spec.ts +++ b/test/event.spec.ts @@ -457,3 +457,23 @@ describe('toConsumer', () => { expect((c as any).cancelAll).to.be.undefined; }); }); + +describe('lastEvent', () => { + it('must be set once emission finished', done => { + const e = new SubEvent(); + e.subscribe(dummy); // TODO: Should not be needed with #33 is resolved? + + const onFinished = (count: number) => { + expect(e.lastEvent).to.eq(123); + }; + + const handler = chai.spy(onFinished); + e.emit(123, {onFinished: handler, schedule: EmitSchedule.async}); + expect(e.lastEvent).to.be.undefined; + + setTimeout(() => { + expect(handler).to.have.been.called.with(1); + done(); + }); + }); +}); diff --git a/typedoc.json b/typedoc.json index 8080510..6be27e2 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,6 +1,6 @@ { "$schema": "https://typedoc.org/schema.json", - "name": "SUB-EVENTS v1.9.1", + "name": "SUB-EVENTS v1.10.0", "out": "./docs", "excludeExternals": true, "excludePrivate": true,