function LoopObservable() {
const listeners = new Set();
this.on = fn => listeners.add(fn);
this.off = fn => listeners.delete(fn);
this.clear = () => listeners.clear();
// This has bad failure modes.
this.send = function(message) {
for (let fn of listeners)
fn(message);
};
};
function EventTargetObservable() {
// Name is arbitrary because we're using a dedicated object. Use a shorter
// name for shorter lookup.
const NAME = '.';
const __ = document.createTextNode(null);
this.on = function(handler) {
__.addEventListener(NAME, event => {
handler(event.detail);
});
};
this.off = function(handler) {
__.removeEventListener(NAME, handler);
};
this.send = function(detail) {
__.dispatchEvent(new CustomEvent(NAME, {detail}));
};
}
function run_test(observable, handlers = 3, calls = 1000) {
for (let i = 0; i < handlers; i++)
observable.on(message => console.log(i, message));
for (let i = 0; i < calls; i++)
observable.send({type: "hello", i});
}
run_test(new LoopObservable());
run_test(new EventTargetObservable());
--enable-precise-memory-info
flag.
Test case name | Result |
---|---|
loop observable | |
EventTarget observable |
Test name | Executions per second |
---|---|
loop observable | 2.5 Ops/sec |
EventTarget observable | 1.3 Ops/sec |
Let's dive into the benchmark and explain what's being tested.
What is being tested?
The benchmark compares two approaches for implementing observables in JavaScript:
EventTarget
implementation from the DOM, which provides a better failure mode for independent listeners.Options being compared
The benchmark compares two options:
EventTarget
implementation from the DOM.Pros and Cons
Loop-based observable (LoopObservable)
Pros:
Cons:
EventTarget-based observable (EventTargetObservable)
Pros:
Cons:
EventTarget
implementation from the DOM.Other considerations
The benchmark also considers the overhead of using the native EventTarget
mechanism, which includes looking up events by their names instead of using direct object references. Additionally, the EventTargetObservable
implementation creates a custom text node to simulate an event dispatcher, which may introduce additional overhead.
Library and purpose
The EventTarget
library is not explicitly mentioned in the benchmark definition, but it's implied that the built-in EventTarget
implementation from the DOM is being hijacked. The purpose of this approach is to provide a better failure mode for independent listeners, as each handler runs in its own execution context.
Special JS feature or syntax
The benchmark uses the CustomEvent
constructor to create custom events, which is not explicitly mentioned in the JavaScript specification. However, it's a widely supported feature in modern browsers.
In summary, the benchmark compares two approaches for implementing observables in JavaScript: the loop-based approach and the event-target based approach. The event-target based approach provides better failure modes and has built-in support for event dispatching and handling, but is more complex to implement.