Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active November 13, 2021 20:52
Show Gist options
  • Save WebReflection/5aaca727bc3b784d43be3704cf65abff to your computer and use it in GitHub Desktop.
Save WebReflection/5aaca727bc3b784d43be3704cf65abff to your computer and use it in GitHub Desktop.
Classes VS DOM Events Handling Benchmark
// Players
class ClickCounter {
constructor() { this.clicks = 0; }
onclick(e) { this.clicks += (e.type === 'click') ? 1 : -1; }
}
class Handler extends ClickCounter {
constructor(currentTarget) {
super();
currentTarget.addEventListener('click', this);
}
}
class DynamicHandler extends Handler {
handleEvent(e) { this['on' + e.type](e); }
}
class StaticHandler extends Handler {
handleEvent(e) { switch (e.type) {
case 'click': return this.onclick(e);
}}
}
// just to rule out hierarchy performance
class Bounder extends ClickCounter {
constructor(currentTarget) { super(); }
}
class ArrowHandler extends Bounder {
constructor(currentTarget) {
super(currentTarget);
this.click = (e) => this.onclick(e);
currentTarget.addEventListener('click', this.click);
}
}
class BoundHandler extends Bounder {
constructor(currentTarget) {
super(currentTarget);
this.onclick = this.onclick.bind(this);
currentTarget.addEventListener('click', this.onclick);
}
}
// Rules
const benchmark = (Class, length = 1000, samples = 5) => {
const currentTarget = button(Class.name);
const instances = new Array(length);
return new Promise(res => setTimeout(res, 500)).then(() => {
let benchName;
let memory;
benchName = `new ${Class.name}(currentTarget)`;
memory = performance.memory.usedJSHeapSize;
console.time(benchName);
for (let i = 0; i < length; i++)
instances[i] = new Class(currentTarget);
console.timeEnd(benchName);
memory = performance.memory.usedJSHeapSize - memory;
if (memory) console.log('memory: ', memory);
const event = new Event('click');
benchName = 'currentTarget.dispatchEvent(clickEvent)';
memory = performance.memory.usedJSHeapSize;
console.time(benchName);
for (let i = 0; i < samples; i++)
currentTarget.dispatchEvent(event);
console.timeEnd(benchName);
memory = performance.memory.usedJSHeapSize - memory;
if (memory) console.log('memory: ', memory);
console.assert(
instances.every(instance => instance.clicks === samples),
`expected ${length} clicks, got ${instances[0].clicks} instead`
);
});
};
// Helpers
const button = textContent => {
const el = document.createElement('button');
el.textContent = textContent;
return document.body.appendChild(el);
};
// Race !
var instances = 10000; // how many instances ?
var dispatches = 10; // how many dispatches ?
Promise
.resolve()
.then(() => benchmark(DynamicHandler, instances, dispatches))
.then(() => benchmark(StaticHandler, instances, dispatches))
.then(() => benchmark(ArrowHandler, instances, dispatches))
.then(() => benchmark(BoundHandler, instances, dispatches))
;
@JasonRammoray
Copy link

I guess instead of "expected ${length} clicks, got ${instances[0].clicks} instead" we should use "expected ${samples} clicks, got ${instances[0].clicks} instead", aren't we?

@JasonRammoray
Copy link

JasonRammoray commented Mar 12, 2018

Just one more thing.
I launched a code of this benchmark (Chrome Version 64.0.3282.186 (Official Build) (64-bit), MacOS High Sierra) and got some different results, which shows an enormous memory consumption for handleEvent.
@WebReflection, can you, please, comment on that?
screen shot 2018-03-12 at 13 51 17

@JasonRammoray
Copy link

JasonRammoray commented Mar 14, 2018

I've posted a question on StackOverflow.
The answer is pretty simple.
handleEvent consumed that much memory due to issue in the benchmark.
The thing is that strings concatenation ('on' + e.type) causes new string allocation in heap on each iteration.
If we just replace this['on' + e.type] to switch(e.type) {case 'click': this.onclick(e);}, then we get results, which are reflecting initial idea regarding benefits of using handleEvent.
screen shot 2018-03-14 at 13 00 34

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment