Skip to content

Commit 2166d1e

Browse files
PanayotCankovAlexander Vakrilov
authored andcommitted
feat(observable): Implement observable .once (#5309)
Node has a handy one-liner method on its EventEmitter called "once". It is similar to "on", but fires a single time and automatically unsubscribes.
1 parent 9d7f0e5 commit 2166d1e

3 files changed

Lines changed: 78 additions & 0 deletions

File tree

‎tns-core-modules/data/observable/observable.d.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ export class Observable {
9595
*/
9696
on(event: "propertyChange", callback: (data: EventData) => void, thisArg?: any);
9797

98+
/**
99+
* Adds one-time listener function for the event named `event`.
100+
* @param event Name of the event to attach to.
101+
* @param callback A function to be called when the specified event is raised.
102+
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
103+
*/
104+
once(event: string, callback: (data: EventData) => void, thisArg?: any);
105+
98106
/**
99107
* Shortcut alias to the removeEventListener method.
100108
*/

‎tns-core-modules/data/observable/observable.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
interface ListenerEntry {
44
callback: (data: EventData) => void;
55
thisArg: any;
6+
once?: true;
67
}
78

89
let _wrappedIndex = 0;
@@ -56,6 +57,11 @@ export class Observable implements ObservableDefinition {
5657
this.addEventListener(eventNames, callback, thisArg);
5758
}
5859

60+
public once(event: string, callback: (data: EventData) => void, thisArg?: any) {
61+
const list = this._getEventList(event, true);
62+
list.push({ callback, thisArg, once: true });
63+
}
64+
5965
public off(eventNames: string, callback?: any, thisArg?: any) {
6066
this.removeEventListener(eventNames, callback, thisArg);
6167
}
@@ -120,6 +126,9 @@ export class Observable implements ObservableDefinition {
120126

121127
for (let i = observers.length - 1; i >= 0; i--) {
122128
let entry = observers[i];
129+
if (entry.once) {
130+
observers.splice(i, 1);
131+
}
123132
if (entry.thisArg) {
124133
entry.callback.apply(entry.thisArg, [data]);
125134
} else {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Observable } from "tns-core-modules/data/observable";
2+
import { assert } from "chai";
3+
4+
describe("observable", () => {
5+
describe("once", () => {
6+
7+
let observable: Observable;
8+
let handler: () => void;
9+
let callCount: number = 0;
10+
11+
beforeEach("create handlers", () => {
12+
handler = function() {
13+
callCount++;
14+
}
15+
observable = new Observable();
16+
observable.once("test", handler);
17+
});
18+
afterEach("reset handlers", () => {
19+
callCount = 0;
20+
handler = null;
21+
observable = null;
22+
});
23+
24+
function notify() {
25+
observable.notify({ eventName: "test", object: observable });
26+
}
27+
function notifyWrong() {
28+
observable.notify({ eventName: "test2", object: observable });
29+
}
30+
31+
it("fires just once", () => {
32+
notify();
33+
notify();
34+
assert.equal(callCount, 1, "Expected the handler to be called exactly once");
35+
});
36+
it("does not fire for other events", () => {
37+
notifyWrong();
38+
assert.equal(callCount, 0, "Expected the handler to not be called, when other events fire");
39+
});
40+
});
41+
42+
describe("once", () => {
43+
it("fire once when fired recursively", () => {
44+
const observable = new Observable();
45+
let callCount1 = 0;
46+
let callCount2 = 0;
47+
const handler2 = function () {
48+
callCount2++;
49+
}
50+
const handler1 = function () {
51+
callCount1++;
52+
observable.once("test", handler2);
53+
observable.notify({ eventName: "test", object: observable });
54+
}
55+
observable.once("test", handler1);
56+
observable.notify({ eventName: "test", object: observable });
57+
assert.equal(callCount1, 1, "Expected the first handler to unsubscribe before being fired and to notify just once");
58+
assert.equal(callCount2, 1, "Expected the second handler to be fired once when recursively notified by the first handler");
59+
});
60+
});
61+
});

0 commit comments

Comments
 (0)