diff --git a/lib/aee.js b/lib/async-emitter.js similarity index 62% rename from lib/aee.js rename to lib/async-emitter.js index 25b0e2cb..aeab165c 100644 --- a/lib/aee.js +++ b/lib/async-emitter.js @@ -2,7 +2,7 @@ const { iter } = require('@metarhia/common'); -class AsyncEventEmitter { +class AsyncEmitter { constructor() { this.events = new Map(); this.wrappers = new Map(); @@ -18,12 +18,18 @@ class AsyncEventEmitter { } once(name, fn) { + if (fn === undefined) { + return new Promise(resolve => { + this.once(name, resolve); + }); + } const wrapper = (...args) => { this.remove(name, wrapper); return fn(...args); }; this.wrappers.set(fn, wrapper); this.on(name, wrapper); + return undefined; } async emit(name, ...args) { @@ -35,20 +41,31 @@ class AsyncEventEmitter { } remove(name, fn) { - const event = this.events.get(name); + const { events, wrappers } = this; + const event = events.get(name); if (!event) return; if (event.has(fn)) event.delete(fn); - const wrapper = this.wrappers.get(fn); + const wrapper = wrappers.get(fn); if (wrapper) { - this.wrappers.delete(fn); + wrappers.delete(fn); event.delete(wrapper); } - if (event.size === 0) this.events.delete(name); + if (event.size === 0) events.delete(name); } clear(name) { - if (name) this.events.delete(name); - else this.events.clear(); + const { events, wrappers } = this; + if (!name) { + events.clear(); + wrappers.clear(); + return; + } + const event = events.get(name); + if (!event) return; + for (const [fn, wrapper] of wrappers.entries()) { + if (event.has(wrapper)) wrappers.delete(fn); + } + events.delete(name); } count(name) { @@ -66,4 +83,4 @@ class AsyncEventEmitter { } } -module.exports = { AsyncEventEmitter }; +module.exports = { AsyncEmitter }; diff --git a/metasync.js b/metasync.js index 5d9a293f..e3f644c5 100644 --- a/metasync.js +++ b/metasync.js @@ -16,7 +16,7 @@ const submodules = [ 'poolify', // Create pool from factory 'queue', // Concurrent queue 'throttle', // Throttling utilities - 'aee', // AsyncEventEmitter + 'async-emitter', // AsyncEmitter ].map(path => require('./lib/' + path)); if (nodeVerion >= 10) { diff --git a/test/aee.js b/test/aee.js deleted file mode 100644 index 6e0a9635..00000000 --- a/test/aee.js +++ /dev/null @@ -1,192 +0,0 @@ -'use strict'; - -const { AsyncEventEmitter } = require('..'); -const metatests = require('metatests'); - -metatests.test('AsyncEventEmitter on/emit', async test => { - const ee = new AsyncEventEmitter(); - let emitted = false; - - ee.on('e1', async (a, b, c, d) => { - test.strictSame(a, 1); - test.strictSame(b, 2); - test.strictSame(c, 3); - test.strictSame(d, 4); - emitted = true; - }); - - await ee.emit('e1', 1, 2, 3, 4); - - test.strictSame(emitted, true); - test.end(); -}); - -metatests.test('AsyncEventEmitter once', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - ee.once('e1', async () => { - count++; - }); - - ee.once('e1', async () => { - count++; - }); - - ee.emit('e1'); - ee.emit('e1'); - - test.strictSame(count, 2); - test.end(); -}); - -metatests.test('AsyncEventEmitter remove', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - const fn = async () => { - count++; - }; - - ee.on('e1', fn); - ee.emit('e1'); - ee.remove('e1', fn); - ee.emit('e1'); - - test.strictSame(count, 1); - test.end(); -}); - -metatests.test('AsyncEventEmitter remove once', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - const fn = async () => { - count++; - }; - - ee.on('e1', fn); - ee.once('e1', fn); - ee.remove('e1', fn); - ee.emit('e1'); - - test.strictSame(count, 0); - test.end(); -}); - -metatests.test('AsyncEventEmitter count', async test => { - const ee = new AsyncEventEmitter(); - - ee.on('e1', async () => {}); - ee.on('e1', async () => {}); - - test.strictSame(ee.count('e1'), 2); - test.end(); -}); - -metatests.test('AsyncEventEmitter clear all', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - ee.on('e1', async () => { - count++; - }); - - ee.clear(); - ee.emit('e1'); - - test.strictSame(count, 0); - test.end(); -}); - -metatests.test('AsyncEventEmitter clear by name', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - ee.on('e1', async () => { - count++; - }); - - ee.clear('e1'); - ee.emit('e1'); - - test.strictSame(count, 0); - test.end(); -}); - -metatests.test('AsyncEventEmitter names', async test => { - const ee = new AsyncEventEmitter(); - - ee.on('e1', () => {}); - ee.on('e1', () => {}); - ee.on('e2', () => {}); - ee.on('e3', () => {}); - - test.strictSame(ee.names(), ['e1', 'e2', 'e3']); - test.end(); -}); - -metatests.test('AsyncEventEmitter listeners', async test => { - const ee = new AsyncEventEmitter(); - - ee.on('e1', () => {}); - ee.on('e1', () => {}); - ee.on('e2', () => {}); - ee.on('e3', () => {}); - - test.strictSame(ee.listeners('e1').size, 2); - test.end(); -}); - -metatests.test('await AsyncEventEmitter', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - - ee.on('e1', async () => { - count += 1; - }); - - await ee.emit('e1'); - - test.strictSame(count, 1); - test.end(); -}); - -metatests.test('await multiple listeners AsyncEventEmitter', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - ee.on('e1', async () => { - count += 1; - }); - - ee.on('e1', async () => { - count += 10; - }); - - ee.on('e1', async () => { - count += 100; - }); - - await ee.emit('e1'); - - test.strictSame(count, 111); - test.end(); -}); - -metatests.test('await multiple events AsyncEventEmitter', async test => { - const ee = new AsyncEventEmitter(); - let count = 0; - ee.on('e1', async () => { - count += 1; - }); - - ee.on('e2', async () => { - count += 10; - }); - - await ee.emit('e1'); - await ee.emit('e2'); - - test.strictSame(count, 11); - test.end(); -}); diff --git a/test/async-emitter.js b/test/async-emitter.js new file mode 100644 index 00000000..3be2e14e --- /dev/null +++ b/test/async-emitter.js @@ -0,0 +1,221 @@ +'use strict'; + +const { AsyncEmitter } = require('..'); +const metatests = require('metatests'); + +metatests.test('AsyncEmitter on/emit', async test => { + const ae = new AsyncEmitter(); + let emitted = false; + + ae.on('e1', async (a, b, c, d) => { + test.strictSame(a, 1); + test.strictSame(b, 2); + test.strictSame(c, 3); + test.strictSame(d, 4); + emitted = true; + }); + + await ae.emit('e1', 1, 2, 3, 4); + + test.strictSame(emitted, true); + test.end(); +}); + +metatests.test('AsyncEmitter once', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + ae.once('e1', async () => { + count++; + }); + + ae.once('e1', async () => { + count++; + }); + + ae.emit('e1'); + ae.emit('e1'); + + test.strictSame(count, 2); + test.end(); +}); + +metatests.test('AsyncEmitter await once', async test => { + const ae = new AsyncEmitter(); + + setTimeout(() => { + ae.emit('e1'); + }, 0); + + await ae.once('e1'); + + test.end(); +}); + +metatests.test('AsyncEmitter remove', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + const fn = async () => { + count++; + }; + + ae.on('e1', fn); + ae.emit('e1'); + ae.remove('e1', fn); + ae.emit('e1'); + + test.strictSame(count, 1); + test.end(); +}); + +metatests.test('AsyncEmitter remove once', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + const fn = async () => { + count++; + }; + + ae.on('e1', fn); + ae.once('e1', fn); + ae.remove('e1', fn); + ae.emit('e1'); + + test.strictSame(count, 0); + test.end(); +}); + +metatests.test('AsyncEmitter count', async test => { + const ae = new AsyncEmitter(); + + ae.on('e1', async () => {}); + ae.on('e1', async () => {}); + + test.strictSame(ae.count('e1'), 2); + test.end(); +}); + +metatests.test('AsyncEmitter clear all', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + ae.on('e1', async () => { + count++; + }); + + ae.clear(); + ae.emit('e1'); + + test.strictSame(count, 0); + test.end(); +}); + +metatests.test('AsyncEmitter clear by name', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + ae.on('e1', async () => { + count++; + }); + + ae.clear('e1'); + ae.emit('e1'); + + test.strictSame(count, 0); + test.end(); +}); + +metatests.test('AsyncEmitter clear once', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + ae.once('e1', async () => { + count++; + }); + + ae.clear('e1'); + ae.emit('e1'); + + test.strictSame(count, 0); + test.strictSame(ae.wrappers.size, 0); + test.strictSame(ae.events.size, 0); + test.end(); +}); + +metatests.test('AsyncEmitter names', async test => { + const ae = new AsyncEmitter(); + + ae.on('e1', () => {}); + ae.on('e1', () => {}); + ae.on('e2', () => {}); + ae.on('e3', () => {}); + + test.strictSame(ae.names(), ['e1', 'e2', 'e3']); + test.end(); +}); + +metatests.test('AsyncEmitter listeners', async test => { + const ae = new AsyncEmitter(); + + ae.on('e1', () => {}); + ae.on('e1', () => {}); + ae.on('e2', () => {}); + ae.on('e3', () => {}); + + test.strictSame(ae.listeners('e1').size, 2); + test.end(); +}); + +metatests.test('await AsyncEmitter', async test => { + const ae = new AsyncEmitter(); + let count = 0; + + ae.on('e1', async () => { + count += 1; + }); + + await ae.emit('e1'); + + test.strictSame(count, 1); + test.end(); +}); + +metatests.test('await multiple listeners AsyncEmitter', async test => { + const ae = new AsyncEmitter(); + let count = 0; + ae.on('e1', async () => { + count += 1; + }); + + ae.on('e1', async () => { + count += 10; + }); + + ae.on('e1', async () => { + count += 100; + }); + + await ae.emit('e1'); + + test.strictSame(count, 111); + test.end(); +}); + +metatests.test('await multiple events AsyncEmitter', async test => { + const ae = new AsyncEmitter(); + let count = 0; + ae.on('e1', async () => { + count += 1; + }); + + ae.on('e2', async () => { + count += 10; + }); + + await ae.emit('e1'); + await ae.emit('e2'); + + test.strictSame(count, 11); + test.end(); +});