Skip to content

Instantly share code, notes, and snippets.

@jpwilliams
Last active September 19, 2017 23:08
Show Gist options
  • Save jpwilliams/0ffec2ad169baecd0497ebdb508fdda9 to your computer and use it in GitHub Desktop.
Save jpwilliams/0ffec2ad169baecd0497ebdb508fdda9 to your computer and use it in GitHub Desktop.
EventEmitter that outputs data on emission

This isn't even really a fair benchmark (testing testing), as the hacky version does more than the proxy, but even without the benchmarks ended up within the same region.

The bottleneck for the Proxy is most definitely the Proxy itself; it ends up plateauing at ~1m ops/sec.

Past that, the next bottleneck is cloning the results array when returning emit. If we ignore the result there and return null, we end up getting around 8-9m ops/sec. Slicing with 0 index is the fastest solution I could find, but there may be ways around this where we create custom arrays for each emission.

const EventEmitter = require('events').EventEmitter
const emitter = DataMitter(new EventEmitter())
emitter.on('data', () => 'foobar')
const results = emitter.emit('data', 'Hello!')
// results = [ 'foobar' ]
const latestData = emitter.on('data', () => 'barqux')
// latestData = [ 'Hello!' ]
function DataMitterProxy (emitter) {
let masterResults = []
const wrapper = (fn) => {
return fn && ((...args) => masterResults.push(fn(...args)))
}
const handler = {
get (target, propKey, receiver) {
const propVal = target[propKey]
if (typeof propVal !== 'function') {
return propVal
}
const origMethod = target[propKey]
return function (...args) {
switch (propKey) {
case 'emit':
masterResults = []
target[propKey].apply(this, args)
return masterResults.concat([])
case 'on':
return target.on.apply(this, [args[0], wrapper(args[1])])
default:
return origMethod.apply(this, args)
}
}
}
}
return new Proxy(emitter, handler)
}
function DataMitterHacky (ee) {
const cache = {}
let emitResults
const wrapper = (fn) => {
return fn && ((...args) => emitResults.push(fn(...args)))
}
ee = ee || new EventEmitter()
const originalFuncs = {
emit: ee.emit,
on: ee.on,
once: ee.once,
prependListener: ee.prependListener,
prependOnceListener: ee.prependOnceListener
}
Object.keys(originalFuncs).forEach((func) => {
originalFuncs[func] = originalFuncs[func] && originalFuncs[func].bind(ee)
})
ee.emit = function emit (eventName, ...args) {
cache[eventName] = args
emitResults = []
originalFuncs.emit(eventName, ...args)
return emitResults.slice(0)
}
listenFuncs.forEach((func) => {
ee[func] = function (eventName, callback) {
originalFuncs[func](eventName, wrapper(callback))
return cache[eventName]
}
})
return ee
}

Emitting benchmarks

Default EventEmitter

Default x 11,978,863 ops/sec ±0.56% (92 runs sampled)
Proxy x 935,010 ops/sec ±1.05% (90 runs sampled)
Hacky x 4,253,500 ops/sec ±0.56% (90 runs sampled)
Fastest is Default

eventemitter3

Default x 28,053,376 ops/sec ±2.07% (87 runs sampled)
Proxy x 987,092 ops/sec ±1.23% (89 runs sampled)
Hacky x 4,181,634 ops/sec ±0.79% (91 runs sampled)
Fastest is Default
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment