├── .npmrc ├── .gitignore ├── index.mjs ├── index.d.mts ├── benchmarks ├── start.sh ├── template.js ├── package.json ├── run │ ├── init.js │ ├── listeners.js │ ├── once.js │ ├── add-remove.js │ ├── hundreds.js │ ├── emit.js │ ├── context.js │ ├── remove-emit.js │ └── emit-multiple-listeners.js └── README.md ├── test ├── test.mjs └── test.js ├── .github └── workflows │ └── ci.yml ├── rollup.config.mjs ├── LICENSE ├── package.json ├── README.md ├── index.d.ts └── index.js /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | dist/ 4 | .tern-port 5 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import EventEmitter from './index.js' 2 | 3 | export { EventEmitter } 4 | export default EventEmitter 5 | -------------------------------------------------------------------------------- /index.d.mts: -------------------------------------------------------------------------------- 1 | import type EventEmitter from "./index.d.ts"; 2 | 3 | export { EventEmitter }; 4 | export default EventEmitter; 5 | -------------------------------------------------------------------------------- /benchmarks/start.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ $# -eq 0 ]; then 4 | echo "usage: $0 " 5 | exit 1 6 | fi 7 | 8 | benchmark=$1 9 | 10 | echo Starting benchmark "${benchmark##*/}" 11 | echo 12 | node "$benchmark" 13 | echo 14 | -------------------------------------------------------------------------------- /test/test.mjs: -------------------------------------------------------------------------------- 1 | import EventEmitterDefault, { EventEmitter } from '../index.mjs'; 2 | 3 | it('exports `EventEmitter` as default export', () => { 4 | new EventEmitterDefault(); 5 | }) 6 | 7 | it('exports `EventEmitter` as a named export', () => { 8 | new EventEmitter(); 9 | }) 10 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v5 12 | - uses: actions/setup-node@v6 13 | with: 14 | node-version: lts/* 15 | - run: npm install 16 | - run: npm test 17 | - run: npm run test-esm 18 | - uses: coverallsapp/github-action@v2 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | -------------------------------------------------------------------------------- /benchmarks/template.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Benchmark related modules. 5 | */ 6 | var benchmark = require('benchmark'); 7 | 8 | /** 9 | * Preparation code. 10 | */ 11 | 12 | ( 13 | new benchmark.Suite() 14 | ).add('', function() { 15 | 16 | }).add('', function() { 17 | 18 | }).on('cycle', function cycle(e) { 19 | console.log(e.target.toString()); 20 | }).on('complete', function completed() { 21 | console.log('Fastest is %s', this.filter('fastest').map('name')); 22 | }).run({ async: true }); 23 | -------------------------------------------------------------------------------- /benchmarks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "benchmarks", 3 | "version": "0.0.0", 4 | "description": "eventemitter3 benchmarks", 5 | "main": "index.js", 6 | "scripts": { 7 | "benchmark": "find run -name '*.js' -exec ./start.sh {} \\;" 8 | }, 9 | "repository": "primus/eventemitter3", 10 | "author": "Arnout Kazemier", 11 | "license": "MIT", 12 | "dependencies": { 13 | "benchmark": "2.1.x", 14 | "contra": "latest", 15 | "drip": "latest", 16 | "event-emitter": "latest", 17 | "eventemitter2": "latest", 18 | "eventemitter3": "0.1.6", 19 | "fastemitter": "latest" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs'; 2 | import terser from '@rollup/plugin-terser'; 3 | 4 | export default [{ 5 | input: './index.mjs', 6 | output: { 7 | file: 'dist/eventemitter3.esm.js', 8 | format: 'es' 9 | }, 10 | plugins: [commonjs()] 11 | }, { 12 | input: './index.mjs', 13 | output: { 14 | compact: true, 15 | file: 'dist/eventemitter3.esm.min.js', 16 | format: 'es', 17 | sourcemap: true 18 | }, 19 | plugins: [commonjs(), terser()] 20 | }, { 21 | input: './index.js', 22 | output: { 23 | file: 'dist/eventemitter3.umd.js', 24 | format: 'umd', 25 | name: 'EventEmitter3' 26 | }, 27 | plugins: [commonjs()] 28 | }, { 29 | input: './index.js', 30 | output: { 31 | compact: true, 32 | file: 'dist/eventemitter3.umd.min.js', 33 | format: 'umd', 34 | name: 'EventEmitter3', 35 | sourcemap: true 36 | }, 37 | plugins: [commonjs(), terser()] 38 | }]; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Arnout Kazemier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmarks/run/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | // 15 | // This is used to prevent the functions below from being transformed into 16 | // noops. 17 | // 18 | var emitter; 19 | 20 | ( 21 | new benchmark.Suite() 22 | ).add('EventEmitter1', function() { 23 | emitter = new EventEmitter1(); 24 | }).add('EventEmitter2', function() { 25 | emitter = new EventEmitter2(); 26 | }).add('EventEmitter3@0.1.6', function() { 27 | emitter = new EventEmitter3(); 28 | }).add('EventEmitter3(master)', function() { 29 | emitter = new Master(); 30 | }).add('Drip', function() { 31 | emitter = new Drip(); 32 | }).add('fastemitter', function() { 33 | emitter = new FE(); 34 | }).add('event-emitter', function() { 35 | emitter = EE(); 36 | }).add('contra/emitter', function() { 37 | emitter = CE(); 38 | }).on('cycle', function cycle(e) { 39 | console.log(e.target.toString()); 40 | }).on('complete', function completed() { 41 | console.log('Fastest is %s', this.filter('fastest').map('name')); 42 | }).run({ async: true }); 43 | -------------------------------------------------------------------------------- /benchmarks/run/listeners.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter1 = require('events').EventEmitter 6 | , EventEmitter3 = require('eventemitter3') 7 | , FE = require('fastemitter') 8 | , Master = require('../../'); 9 | 10 | var MAX_LISTENERS = Math.pow(2, 32) - 1; 11 | 12 | function handle() { 13 | if (arguments.length > 100) console.log('damn'); 14 | } 15 | 16 | var ee1 = new EventEmitter1() 17 | , ee3 = new EventEmitter3() 18 | , master = new Master() 19 | , fe = new FE(); 20 | 21 | ee1.setMaxListeners(MAX_LISTENERS); 22 | fe.setMaxListeners(MAX_LISTENERS); 23 | 24 | for (var i = 0; i < 25; i++) { 25 | ee1.on('event', handle); 26 | ee3.on('event', handle); 27 | master.on('event', handle); 28 | fe.on('event', handle); 29 | } 30 | 31 | // 32 | // eventemitter2 doesn't correctly handle listeners as they can be removed by 33 | // doing `ee2.listeners('event').length = 0;`. Same counts for Drip. 34 | // 35 | // event-emitter and contra/emitter do not implement `listeners`. 36 | // 37 | 38 | ( 39 | new benchmark.Suite() 40 | ).add('EventEmitter1', function () { 41 | ee1.listeners('event'); 42 | }).add('EventEmitter3@0.1.6', function() { 43 | ee3.listeners('event'); 44 | }).add('EventEmitter3(master)', function() { 45 | master.listeners('event'); 46 | }).add('fastemitter', function() { 47 | fe.listeners('event'); 48 | }).on('cycle', function cycle(e) { 49 | console.log(e.target.toString()); 50 | }).on('complete', function completed() { 51 | console.log('Fastest is %s', this.filter('fastest').map('name')); 52 | }).run({ async: true }); 53 | -------------------------------------------------------------------------------- /benchmarks/run/once.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | function handle() { 15 | if (arguments.length > 100) console.log('damn'); 16 | } 17 | 18 | var ee1 = new EventEmitter1() 19 | , ee2 = new EventEmitter2() 20 | , ee3 = new EventEmitter3() 21 | , master = new Master() 22 | , drip = new Drip() 23 | , fe = new FE() 24 | , ce = CE() 25 | , ee = EE(); 26 | 27 | ( 28 | new benchmark.Suite() 29 | ).add('EventEmitter1', function() { 30 | ee1.once('foo', handle).emit('foo'); 31 | }).add('EventEmitter2', function() { 32 | ee2.once('foo', handle).emit('foo'); 33 | }).add('EventEmitter3@0.1.6', function() { 34 | ee3.once('foo', handle).emit('foo'); 35 | }).add('EventEmitter3(master)', function() { 36 | master.once('foo', handle).emit('foo'); 37 | }).add('Drip', function() { 38 | drip.once('foo', handle).emit('foo'); 39 | }).add('fastemitter', function() { 40 | fe.once('foo', handle).emit('foo'); 41 | }).add('event-emitter', function() { 42 | ee.once('foo', handle).emit('foo'); 43 | }).add('contra/emitter', function() { 44 | ce.once('foo', handle).emit('foo'); 45 | }).on('cycle', function cycle(e) { 46 | console.log(e.target.toString()); 47 | }).on('complete', function completed() { 48 | console.log('Fastest is %s', this.filter('fastest').map('name')); 49 | }).run({ async: true }); 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eventemitter3", 3 | "version": "5.0.1", 4 | "description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.", 5 | "exports": { 6 | ".": { 7 | "import": "./index.mjs", 8 | "require": "./index.js" 9 | }, 10 | "./package.json": "./package.json" 11 | }, 12 | "main": "index.js", 13 | "types": "index.d.ts", 14 | "scripts": { 15 | "rollup": "rm -rf dist && rollup -c", 16 | "benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;", 17 | "test": "c8 --reporter=lcov --reporter=text mocha test/test.js", 18 | "test-esm": "mocha test/test.mjs", 19 | "prepublishOnly": "npm run rollup" 20 | }, 21 | "files": [ 22 | "index.js", 23 | "index.mjs", 24 | "index.d.ts", 25 | "index.d.mts", 26 | "dist" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "git://github.com/primus/eventemitter3.git" 31 | }, 32 | "keywords": [ 33 | "EventEmitter", 34 | "EventEmitter2", 35 | "EventEmitter3", 36 | "Events", 37 | "addEventListener", 38 | "addListener", 39 | "emit", 40 | "emits", 41 | "emitter", 42 | "event", 43 | "once", 44 | "pub/sub", 45 | "publish", 46 | "reactor", 47 | "subscribe" 48 | ], 49 | "author": "Arnout Kazemier", 50 | "license": "MIT", 51 | "bugs": { 52 | "url": "https://github.com/primus/eventemitter3/issues" 53 | }, 54 | "devDependencies": { 55 | "@rollup/plugin-commonjs": "^29.0.0", 56 | "@rollup/plugin-terser": "^0.4.0", 57 | "assume": "^2.2.0", 58 | "c8": "^10.1.3", 59 | "mocha": "^11.7.5", 60 | "rollup": "^4.5.2" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /benchmarks/run/add-remove.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | function handle() { 15 | if (arguments.length > 100) console.log('damn'); 16 | } 17 | 18 | var ee1 = new EventEmitter1() 19 | , ee2 = new EventEmitter2() 20 | , ee3 = new EventEmitter3() 21 | , master = new Master() 22 | , drip = new Drip() 23 | , fe = new FE() 24 | , ce = CE() 25 | , ee = EE(); 26 | 27 | ( 28 | new benchmark.Suite() 29 | ).add('EventEmitter1', function() { 30 | ee1.on('foo', handle); 31 | ee1.removeListener('foo', handle); 32 | }).add('EventEmitter2', function() { 33 | ee2.on('foo', handle); 34 | ee2.removeListener('foo', handle); 35 | }).add('EventEmitter3@0.1.6', function() { 36 | ee3.on('foo', handle); 37 | ee3.removeListener('foo', handle); 38 | }).add('EventEmitter3(master)', function() { 39 | master.on('foo', handle); 40 | master.removeListener('foo', handle); 41 | }).add('Drip', function() { 42 | drip.on('foo', handle); 43 | drip.removeListener('foo', handle); 44 | }).add('fastemitter', function() { 45 | fe.on('foo', handle); 46 | fe.removeListener('foo', handle); 47 | }).add('event-emitter', function() { 48 | ee.on('foo', handle); 49 | ee.off('foo', handle); 50 | }).add('contra/emitter', function() { 51 | ce.on('foo', handle); 52 | ce.off('foo', handle); 53 | }).on('cycle', function cycle(e) { 54 | console.log(e.target.toString()); 55 | }).on('complete', function completed() { 56 | console.log('Fastest is %s', this.filter('fastest').map('name')); 57 | }).run({ async: true }); 58 | -------------------------------------------------------------------------------- /benchmarks/run/hundreds.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | function foo() { 15 | if (arguments.length > 100) console.log('damn'); 16 | 17 | return 1; 18 | } 19 | 20 | var ee1 = new EventEmitter1() 21 | , ee2 = new EventEmitter2() 22 | , ee3 = new EventEmitter3() 23 | , master = new Master() 24 | , drip = new Drip() 25 | , fe = new FE() 26 | , ce = CE() 27 | , ee = EE() 28 | , j, i; 29 | 30 | for (i = 0; i < 10; i++) { 31 | for (j = 0; j < 10; j++) { 32 | ce.on('event:' + i, foo); 33 | ee.on('event:' + i, foo); 34 | fe.on('event:' + i, foo); 35 | ee1.on('event:' + i, foo); 36 | ee2.on('event:' + i, foo); 37 | ee3.on('event:' + i, foo); 38 | drip.on('event:' + i, foo); 39 | master.on('event:' + i, foo); 40 | } 41 | } 42 | 43 | ( 44 | new benchmark.Suite() 45 | ).add('EventEmitter1', function() { 46 | for (i = 0; i < 10; i++) { 47 | ee1.emit('event:' + i); 48 | } 49 | }).add('EventEmitter2', function() { 50 | for (i = 0; i < 10; i++) { 51 | ee2.emit('event:' + i); 52 | } 53 | }).add('EventEmitter3@0.1.6', function() { 54 | for (i = 0; i < 10; i++) { 55 | ee3.emit('event:' + i); 56 | } 57 | }).add('EventEmitter3(master)', function() { 58 | for (i = 0; i < 10; i++) { 59 | master.emit('event:' + i); 60 | } 61 | }).add('Drip', function() { 62 | for (i = 0; i < 10; i++) { 63 | drip.emit('event:' + i); 64 | } 65 | }).add('fastemitter', function() { 66 | for (i = 0; i < 10; i++) { 67 | fe.emit('event:' + i); 68 | } 69 | }).add('event-emitter', function() { 70 | for (i = 0; i < 10; i++) { 71 | ee.emit('event:' + i); 72 | } 73 | }).add('contra/emitter', function() { 74 | for (i = 0; i < 10; i++) { 75 | ce.emit('event:' + i); 76 | } 77 | }).on('cycle', function cycle(e) { 78 | console.log(e.target.toString()); 79 | }).on('complete', function completed() { 80 | console.log('Fastest is %s', this.filter('fastest').map('name')); 81 | }).run({ async: true }); 82 | -------------------------------------------------------------------------------- /benchmarks/run/emit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | function handle() { 15 | if (arguments.length > 100) console.log('damn'); 16 | } 17 | 18 | var ee1 = new EventEmitter1() 19 | , ee2 = new EventEmitter2() 20 | , ee3 = new EventEmitter3() 21 | , master = new Master() 22 | , drip = new Drip() 23 | , fe = new FE() 24 | , ce = CE() 25 | , ee = EE(); 26 | 27 | ee.on('foo', handle); 28 | fe.on('foo', handle); 29 | ee3.on('foo', handle); 30 | ee2.on('foo', handle); 31 | ee1.on('foo', handle); 32 | drip.on('foo', handle); 33 | master.on('foo', handle); 34 | ce.on('foo', handle); 35 | 36 | ( 37 | new benchmark.Suite() 38 | ).add('EventEmitter1', function() { 39 | ee1.emit('foo'); 40 | ee1.emit('foo', 'bar'); 41 | ee1.emit('foo', 'bar', 'baz'); 42 | ee1.emit('foo', 'bar', 'baz', 'boom'); 43 | }).add('EventEmitter2', function() { 44 | ee2.emit('foo'); 45 | ee2.emit('foo', 'bar'); 46 | ee2.emit('foo', 'bar', 'baz'); 47 | ee2.emit('foo', 'bar', 'baz', 'boom'); 48 | }).add('EventEmitter3@0.1.6', function() { 49 | ee3.emit('foo'); 50 | ee3.emit('foo', 'bar'); 51 | ee3.emit('foo', 'bar', 'baz'); 52 | ee3.emit('foo', 'bar', 'baz', 'boom'); 53 | }).add('EventEmitter3(master)', function() { 54 | master.emit('foo'); 55 | master.emit('foo', 'bar'); 56 | master.emit('foo', 'bar', 'baz'); 57 | master.emit('foo', 'bar', 'baz', 'boom'); 58 | }).add('Drip', function() { 59 | drip.emit('foo'); 60 | drip.emit('foo', 'bar'); 61 | drip.emit('foo', 'bar', 'baz'); 62 | drip.emit('foo', 'bar', 'baz', 'boom'); 63 | }).add('fastemitter', function() { 64 | fe.emit('foo'); 65 | fe.emit('foo', 'bar'); 66 | fe.emit('foo', 'bar', 'baz'); 67 | fe.emit('foo', 'bar', 'baz', 'boom'); 68 | }).add('event-emitter', function() { 69 | ee.emit('foo'); 70 | ee.emit('foo', 'bar'); 71 | ee.emit('foo', 'bar', 'baz'); 72 | ee.emit('foo', 'bar', 'baz', 'boom'); 73 | }).add('contra/emitter', function() { 74 | ce.emit('foo'); 75 | ce.emit('foo', 'bar'); 76 | ce.emit('foo', 'bar', 'baz'); 77 | ce.emit('foo', 'bar', 'baz', 'boom'); 78 | }).on('cycle', function cycle(e) { 79 | console.log(e.target.toString()); 80 | }).on('complete', function completed() { 81 | console.log('Fastest is %s', this.filter('fastest').map('name')); 82 | }).run({ async: true }); 83 | -------------------------------------------------------------------------------- /benchmarks/run/context.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , FE = require('fastemitter') 12 | , Master = require('../../'); 13 | 14 | var ctx = { foo: 'bar' }; 15 | 16 | function handle() { 17 | if (arguments.length > 100) console.log('damn'); 18 | } 19 | 20 | var ee1 = new EventEmitter1() 21 | , ee2 = new EventEmitter2() 22 | , ee3 = new EventEmitter3() 23 | , master = new Master() 24 | , drip = new Drip() 25 | , fe = new FE() 26 | , ce = CE() 27 | , ee = EE(); 28 | 29 | ee3.on('foo', handle, ctx); 30 | ee2.on('foo', handle.bind(ctx)); 31 | ee1.on('foo', handle.bind(ctx)); 32 | drip.on('foo', handle.bind(ctx)); 33 | master.on('foo', handle, ctx); 34 | ee.on('foo', handle.bind(ctx)); 35 | fe.on('foo', handle.bind(ctx)); 36 | ce.on('foo', handle.bind(ctx)); 37 | 38 | ( 39 | new benchmark.Suite() 40 | ).add('EventEmitter1', function() { 41 | ee1.emit('foo'); 42 | ee1.emit('foo', 'bar'); 43 | ee1.emit('foo', 'bar', 'baz'); 44 | ee1.emit('foo', 'bar', 'baz', 'boom'); 45 | }).add('EventEmitter2', function() { 46 | ee2.emit('foo'); 47 | ee2.emit('foo', 'bar'); 48 | ee2.emit('foo', 'bar', 'baz'); 49 | ee2.emit('foo', 'bar', 'baz', 'boom'); 50 | }).add('EventEmitter3@0.1.6', function() { 51 | ee3.emit('foo'); 52 | ee3.emit('foo', 'bar'); 53 | ee3.emit('foo', 'bar', 'baz'); 54 | ee3.emit('foo', 'bar', 'baz', 'boom'); 55 | }).add('EventEmitter3(master)', function() { 56 | master.emit('foo'); 57 | master.emit('foo', 'bar'); 58 | master.emit('foo', 'bar', 'baz'); 59 | master.emit('foo', 'bar', 'baz', 'boom'); 60 | }).add('Drip', function() { 61 | drip.emit('foo'); 62 | drip.emit('foo', 'bar'); 63 | drip.emit('foo', 'bar', 'baz'); 64 | drip.emit('foo', 'bar', 'baz', 'boom'); 65 | }).add('fastemitter', function() { 66 | fe.emit('foo'); 67 | fe.emit('foo', 'bar'); 68 | fe.emit('foo', 'bar', 'baz'); 69 | fe.emit('foo', 'bar', 'baz', 'boom'); 70 | }).add('event-emitter', function() { 71 | ee.emit('foo'); 72 | ee.emit('foo', 'bar'); 73 | ee.emit('foo', 'bar', 'baz'); 74 | ee.emit('foo', 'bar', 'baz', 'boom'); 75 | }).add('contra/emitter', function() { 76 | ce.emit('foo'); 77 | ce.emit('foo', 'bar'); 78 | ce.emit('foo', 'bar', 'baz'); 79 | ce.emit('foo', 'bar', 'baz', 'boom'); 80 | }).on('cycle', function cycle(e) { 81 | console.log(e.target.toString()); 82 | }).on('complete', function completed() { 83 | console.log('Fastest is %s', this.filter('fastest').map('name')); 84 | }).run({ async: true }); 85 | -------------------------------------------------------------------------------- /benchmarks/run/remove-emit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , Drip = require('drip').EventEmitter 9 | , CE = require('contra/emitter') 10 | , EE = require('event-emitter') 11 | , Master = require('../../'); 12 | 13 | function handle() { 14 | if (arguments.length > 100) console.log('damn'); 15 | } 16 | 17 | var ee1 = new EventEmitter1() 18 | , ee2 = new EventEmitter2() 19 | , ee3 = new EventEmitter3() 20 | , master = new Master() 21 | , drip = new Drip() 22 | , ce = CE() 23 | , ee = EE(); 24 | 25 | [ee1, ee2, ee3, master, drip, ee, ce].forEach(function ohai(emitter) { 26 | emitter.on('foo', handle); 27 | 28 | // 29 | // We add and remove a listener to see if the event emitter implementation is 30 | // de-optimized because it deletes items from an object etc. 31 | // 32 | emitter.on('ohai', ohai); 33 | if (emitter.removeListener) emitter.removeListener('ohai', ohai); 34 | else if (emitter.off) emitter.off('ohai', ohai); 35 | else throw new Error('No proper remove implementation'); 36 | }); 37 | 38 | // 39 | // FastEmitter is omitted as it throws an error. 40 | // 41 | 42 | ( 43 | new benchmark.Suite() 44 | ).add('EventEmitter1', function() { 45 | ee1.emit('foo'); 46 | ee1.emit('foo', 'bar'); 47 | ee1.emit('foo', 'bar', 'baz'); 48 | ee1.emit('foo', 'bar', 'baz', 'boom'); 49 | }).add('EventEmitter2', function() { 50 | ee2.emit('foo'); 51 | ee2.emit('foo', 'bar'); 52 | ee2.emit('foo', 'bar', 'baz'); 53 | ee2.emit('foo', 'bar', 'baz', 'boom'); 54 | }).add('EventEmitter3@0.1.6', function() { 55 | ee3.emit('foo'); 56 | ee3.emit('foo', 'bar'); 57 | ee3.emit('foo', 'bar', 'baz'); 58 | ee3.emit('foo', 'bar', 'baz', 'boom'); 59 | }).add('EventEmitter3(master)', function() { 60 | master.emit('foo'); 61 | master.emit('foo', 'bar'); 62 | master.emit('foo', 'bar', 'baz'); 63 | master.emit('foo', 'bar', 'baz', 'boom'); 64 | }).add('Drip', function() { 65 | drip.emit('foo'); 66 | drip.emit('foo', 'bar'); 67 | drip.emit('foo', 'bar', 'baz'); 68 | drip.emit('foo', 'bar', 'baz', 'boom'); 69 | }).add('event-emitter', function() { 70 | ee.emit('foo'); 71 | ee.emit('foo', 'bar'); 72 | ee.emit('foo', 'bar', 'baz'); 73 | ee.emit('foo', 'bar', 'baz', 'boom'); 74 | }).add('contra/emitter', function() { 75 | ce.emit('foo'); 76 | ce.emit('foo', 'bar'); 77 | ce.emit('foo', 'bar', 'baz'); 78 | ce.emit('foo', 'bar', 'baz', 'boom'); 79 | }).on('cycle', function cycle(e) { 80 | console.log(e.target.toString()); 81 | }).on('complete', function completed() { 82 | console.log('Fastest is %s', this.filter('fastest').map('name')); 83 | }).run({ async: true }); 84 | -------------------------------------------------------------------------------- /benchmarks/run/emit-multiple-listeners.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var benchmark = require('benchmark'); 4 | 5 | var EventEmitter2 = require('eventemitter2').EventEmitter2 6 | , EventEmitter1 = require('events').EventEmitter 7 | , EventEmitter3 = require('eventemitter3') 8 | , CE = require('contra/emitter') 9 | , EE = require('event-emitter') 10 | , FE = require('fastemitter') 11 | , Master = require('../../'); 12 | 13 | function foo() { 14 | if (arguments.length > 100) console.log('damn'); 15 | 16 | return 1; 17 | } 18 | 19 | function bar() { 20 | if (arguments.length > 100) console.log('damn'); 21 | 22 | return false; 23 | } 24 | 25 | function baz() { 26 | if (arguments.length > 100) console.log('damn'); 27 | 28 | return true; 29 | } 30 | 31 | var ee1 = new EventEmitter1() 32 | , ee2 = new EventEmitter2() 33 | , ee3 = new EventEmitter3() 34 | , master = new Master() 35 | , fe = new FE() 36 | , ce = CE() 37 | , ee = EE(); 38 | 39 | ce.on('foo', foo).on('foo', bar).on('foo', baz); 40 | ee.on('foo', foo).on('foo', bar).on('foo', baz); 41 | fe.on('foo', foo).on('foo', bar).on('foo', baz); 42 | ee3.on('foo', foo).on('foo', bar).on('foo', baz); 43 | ee2.on('foo', foo).on('foo', bar).on('foo', baz); 44 | ee1.on('foo', foo).on('foo', bar).on('foo', baz); 45 | master.on('foo', foo).on('foo', bar).on('foo', baz); 46 | 47 | // 48 | // Drip is omitted as it throws an error. 49 | // Ref: https://github.com/qualiancy/drip/pull/4 50 | // 51 | 52 | ( 53 | new benchmark.Suite() 54 | ).add('EventEmitter1', function() { 55 | ee1.emit('foo'); 56 | ee1.emit('foo', 'bar'); 57 | ee1.emit('foo', 'bar', 'baz'); 58 | ee1.emit('foo', 'bar', 'baz', 'boom'); 59 | }).add('EventEmitter2', function() { 60 | ee2.emit('foo'); 61 | ee2.emit('foo', 'bar'); 62 | ee2.emit('foo', 'bar', 'baz'); 63 | ee2.emit('foo', 'bar', 'baz', 'boom'); 64 | }).add('EventEmitter3@0.1.6', function() { 65 | ee3.emit('foo'); 66 | ee3.emit('foo', 'bar'); 67 | ee3.emit('foo', 'bar', 'baz'); 68 | ee3.emit('foo', 'bar', 'baz', 'boom'); 69 | }).add('EventEmitter3(master)', function() { 70 | master.emit('foo'); 71 | master.emit('foo', 'bar'); 72 | master.emit('foo', 'bar', 'baz'); 73 | master.emit('foo', 'bar', 'baz', 'boom'); 74 | }).add('fastemitter', function() { 75 | fe.emit('foo'); 76 | fe.emit('foo', 'bar'); 77 | fe.emit('foo', 'bar', 'baz'); 78 | fe.emit('foo', 'bar', 'baz', 'boom'); 79 | }).add('event-emitter', function() { 80 | ee.emit('foo'); 81 | ee.emit('foo', 'bar'); 82 | ee.emit('foo', 'bar', 'baz'); 83 | ee.emit('foo', 'bar', 'baz', 'boom'); 84 | }).add('contra/emitter', function() { 85 | ce.emit('foo'); 86 | ce.emit('foo', 'bar'); 87 | ce.emit('foo', 'bar', 'baz'); 88 | ce.emit('foo', 'bar', 'baz', 'boom'); 89 | }).on('cycle', function cycle(e) { 90 | console.log(e.target.toString()); 91 | }).on('complete', function completed() { 92 | console.log('Fastest is %s', this.filter('fastest').map('name')); 93 | }).run({ async: true }); 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EventEmitter3 2 | 3 | [![Version npm](https://img.shields.io/npm/v/eventemitter3.svg?style=flat-square)](https://www.npmjs.com/package/eventemitter3)[![CI](https://img.shields.io/github/actions/workflow/status/primus/eventemitter3/ci.yml?branch=master&label=CI&style=flat-square)](https://github.com/primus/eventemitter3/actions?query=workflow%3ACI+branch%3Amaster)[![Coverage Status](https://img.shields.io/coveralls/primus/eventemitter3/master.svg?style=flat-square)](https://coveralls.io/r/primus/eventemitter3?branch=master) 4 | 5 | EventEmitter3 is a high performance EventEmitter. It has been micro-optimized 6 | for various of code paths making this, one of, if not the fastest EventEmitter 7 | available for Node.js and browsers. The module is API compatible with the 8 | EventEmitter that ships by default with Node.js but there are some slight 9 | differences: 10 | 11 | - Domain support has been removed. 12 | - We do not `throw` an error when you emit an `error` event and nobody is 13 | listening. 14 | - The `newListener` and `removeListener` events have been removed as they 15 | are useful only in some uncommon use-cases. 16 | - The `setMaxListeners`, `getMaxListeners`, `prependListener` and 17 | `prependOnceListener` methods are not available. 18 | - Support for custom context for events so there is no need to use `fn.bind`. 19 | - The `removeListener` method removes all matching listeners, not only the 20 | first. 21 | 22 | It's a drop in replacement for existing EventEmitters, but just faster. Free 23 | performance, who wouldn't want that? The EventEmitter is written in EcmaScript 3 24 | so it will work in the oldest browsers and node versions that you need to 25 | support. 26 | 27 | ## Installation 28 | 29 | ```bash 30 | $ npm install --save eventemitter3 31 | ``` 32 | 33 | ## CDN 34 | 35 | Recommended CDN: 36 | 37 | ```text 38 | https://unpkg.com/eventemitter3@latest/dist/eventemitter3.umd.min.js 39 | ``` 40 | 41 | ## Usage 42 | 43 | After installation the only thing you need to do is require the module: 44 | 45 | ```js 46 | var EventEmitter = require('eventemitter3'); 47 | ``` 48 | 49 | And you're ready to create your own EventEmitter instances. For the API 50 | documentation, please follow the official Node.js documentation: 51 | 52 | http://nodejs.org/api/events.html 53 | 54 | ### Contextual emits 55 | 56 | We've upgraded the API of the `EventEmitter.on`, `EventEmitter.once` and 57 | `EventEmitter.removeListener` to accept an extra argument which is the `context` 58 | or `this` value that should be set for the emitted events. This means you no 59 | longer have the overhead of an event that required `fn.bind` in order to get a 60 | custom `this` value. 61 | 62 | ```js 63 | var EE = new EventEmitter() 64 | , context = { foo: 'bar' }; 65 | 66 | function emitted() { 67 | console.log(this === context); // true 68 | } 69 | 70 | EE.once('event-name', emitted, context); 71 | EE.on('another-event', emitted, context); 72 | EE.removeListener('another-event', emitted, context); 73 | ``` 74 | 75 | ### Tests and benchmarks 76 | 77 | To run tests run `npm test`. To run the benchmarks run `npm run benchmark`. 78 | 79 | Tests and benchmarks are not included in the npm package. If you want to play 80 | with them you have to clone the GitHub repository. Note that you will have to 81 | run an additional `npm i` in the benchmarks folder before `npm run benchmark`. 82 | 83 | ## License 84 | 85 | [MIT](LICENSE) 86 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Minimal `EventEmitter` interface that is molded against the Node.js 3 | * `EventEmitter` interface. 4 | */ 5 | declare class EventEmitter< 6 | EventTypes extends EventEmitter.ValidEventTypes = string | symbol, 7 | Context extends any = any 8 | > { 9 | static prefixed: string | boolean; 10 | 11 | /** 12 | * Return an array listing the events for which the emitter has registered 13 | * listeners. 14 | */ 15 | eventNames(): Array>; 16 | 17 | /** 18 | * Return the listeners registered for a given event. 19 | */ 20 | listeners>( 21 | event: T 22 | ): Array>; 23 | 24 | /** 25 | * Return the number of listeners listening to a given event. 26 | */ 27 | listenerCount(event: EventEmitter.EventNames): number; 28 | 29 | /** 30 | * Calls each of the listeners registered for a given event. 31 | */ 32 | emit>( 33 | event: T, 34 | ...args: EventEmitter.EventArgs 35 | ): boolean; 36 | 37 | /** 38 | * Add a listener for a given event. 39 | */ 40 | on>( 41 | event: T, 42 | fn: EventEmitter.EventListener, 43 | context?: Context 44 | ): this; 45 | addListener>( 46 | event: T, 47 | fn: EventEmitter.EventListener, 48 | context?: Context 49 | ): this; 50 | 51 | /** 52 | * Add a one-time listener for a given event. 53 | */ 54 | once>( 55 | event: T, 56 | fn: EventEmitter.EventListener, 57 | context?: Context 58 | ): this; 59 | 60 | /** 61 | * Remove the listeners of a given event. 62 | */ 63 | removeListener>( 64 | event: T, 65 | fn?: EventEmitter.EventListener, 66 | context?: Context, 67 | once?: boolean 68 | ): this; 69 | off>( 70 | event: T, 71 | fn?: EventEmitter.EventListener, 72 | context?: Context, 73 | once?: boolean 74 | ): this; 75 | 76 | /** 77 | * Remove all listeners, or those of the specified event. 78 | */ 79 | removeAllListeners(event?: EventEmitter.EventNames): this; 80 | } 81 | 82 | declare namespace EventEmitter { 83 | export interface ListenerFn { 84 | (...args: Args): void; 85 | } 86 | 87 | export interface EventEmitterStatic { 88 | new < 89 | EventTypes extends ValidEventTypes = string | symbol, 90 | Context = any 91 | >(): EventEmitter; 92 | } 93 | 94 | /** 95 | * `object` should be in either of the following forms: 96 | * ``` 97 | * interface EventTypes { 98 | * 'event-with-parameters': any[] 99 | * 'event-with-example-handler': (...args: any[]) => void 100 | * } 101 | * ``` 102 | */ 103 | export type ValidEventTypes = string | symbol | object; 104 | 105 | export type EventNames = T extends string | symbol 106 | ? T 107 | : keyof T; 108 | 109 | export type ArgumentMap = { 110 | [K in keyof T]: T[K] extends (...args: any[]) => void 111 | ? Parameters 112 | : T[K] extends any[] 113 | ? T[K] 114 | : any[]; 115 | }; 116 | 117 | export type EventListener< 118 | T extends ValidEventTypes, 119 | K extends EventNames 120 | > = T extends string | symbol 121 | ? (...args: any[]) => void 122 | : ( 123 | ...args: ArgumentMap>[Extract] 124 | ) => void; 125 | 126 | export type EventArgs< 127 | T extends ValidEventTypes, 128 | K extends EventNames 129 | > = Parameters>; 130 | 131 | export const EventEmitter: EventEmitterStatic; 132 | } 133 | 134 | module.exports = EventEmitter; 135 | -------------------------------------------------------------------------------- /benchmarks/README.md: -------------------------------------------------------------------------------- 1 | Starting benchmark listeners.js 2 | 3 | ``` 4 | EventEmitter1 x 7,572,173 ops/sec ±0.93% (92 runs sampled) 5 | EventEmitter3@0.1.6 x 7,990,937 ops/sec ±0.31% (93 runs sampled) 6 | EventEmitter3(master) x 8,140,661 ops/sec ±0.44% (91 runs sampled) 7 | fastemitter x 5,554,014 ops/sec ±0.37% (88 runs sampled) 8 | Fastest is EventEmitter3(master) 9 | ``` 10 | 11 | Starting benchmark init.js 12 | 13 | ``` 14 | EventEmitter1 x 15,101,015 ops/sec ±1.66% (89 runs sampled) 15 | EventEmitter2 x 23,365,046 ops/sec ±0.50% (90 runs sampled) 16 | EventEmitter3@0.1.6 x 32,657,186 ops/sec ±0.61% (88 runs sampled) 17 | EventEmitter3(master) x 28,325,527 ops/sec ±0.25% (95 runs sampled) 18 | Drip x 33,751,683 ops/sec ±1.31% (84 runs sampled) 19 | fastemitter x 11,738,645 ops/sec ±0.35% (86 runs sampled) 20 | event-emitter x 22,670,613 ops/sec ±0.23% (95 runs sampled) 21 | contra/emitter x 1,102,480 ops/sec ±0.43% (91 runs sampled) 22 | Fastest is Drip 23 | ``` 24 | 25 | Starting benchmark remove-emit.js 26 | 27 | ``` 28 | EventEmitter1 x 6,175,136 ops/sec ±0.44% (90 runs sampled) 29 | EventEmitter2 x 5,767,426 ops/sec ±1.08% (87 runs sampled) 30 | EventEmitter3@0.1.6 x 10,269,081 ops/sec ±0.91% (88 runs sampled) 31 | EventEmitter3(master) x 11,549,113 ops/sec ±0.67% (88 runs sampled) 32 | Drip x 5,795,926 ops/sec ±1.20% (88 runs sampled) 33 | event-emitter x 4,515,241 ops/sec ±0.47% (90 runs sampled) 34 | contra/emitter x 403,089 ops/sec ±0.79% (90 runs sampled) 35 | Fastest is EventEmitter3(master) 36 | ``` 37 | 38 | Starting benchmark emit-multiple-listeners.js 39 | 40 | ``` 41 | EventEmitter1 x 2,507,915 ops/sec ±0.53% (93 runs sampled) 42 | EventEmitter2 x 1,126,907 ops/sec ±0.29% (94 runs sampled) 43 | EventEmitter3@0.1.6 x 2,377,802 ops/sec ±0.92% (89 runs sampled) 44 | EventEmitter3(master) x 2,904,038 ops/sec ±1.06% (94 runs sampled) 45 | fastemitter x 2,804,437 ops/sec ±0.30% (92 runs sampled) 46 | event-emitter x 944,055 ops/sec ±0.21% (94 runs sampled) 47 | contra/emitter x 363,577 ops/sec ±0.46% (90 runs sampled) 48 | Fastest is EventEmitter3(master) 49 | ``` 50 | 51 | Starting benchmark context.js 52 | 53 | ``` 54 | EventEmitter1 x 5,944,004 ops/sec ±0.40% (92 runs sampled) 55 | EventEmitter2 x 6,142,119 ops/sec ±0.33% (88 runs sampled) 56 | EventEmitter3@0.1.6 x 10,021,281 ops/sec ±0.34% (90 runs sampled) 57 | EventEmitter3(master) x 12,449,276 ops/sec ±0.85% (87 runs sampled) 58 | Drip x 6,112,535 ops/sec ±1.07% (94 runs sampled) 59 | fastemitter x 5,351,583 ops/sec ±0.33% (91 runs sampled) 60 | event-emitter x 4,664,745 ops/sec ±0.89% (91 runs sampled) 61 | contra/emitter x 432,819 ops/sec ±0.45% (93 runs sampled) 62 | Fastest is EventEmitter3(master) 63 | ``` 64 | 65 | Starting benchmark once.js 66 | 67 | ``` 68 | EventEmitter1 x 3,570,512 ops/sec ±0.43% (90 runs sampled) 69 | EventEmitter2 x 2,375,433 ops/sec ±1.28% (88 runs sampled) 70 | EventEmitter3@0.1.6 x 6,800,263 ops/sec ±0.62% (89 runs sampled) 71 | EventEmitter3(master) x 11,971,608 ops/sec ±1.55% (86 runs sampled) 72 | Drip x 6,555,088 ops/sec ±0.80% (87 runs sampled) 73 | fastemitter x 6,320,868 ops/sec ±0.41% (87 runs sampled) 74 | event-emitter x 2,251,961 ops/sec ±0.26% (90 runs sampled) 75 | contra/emitter x 1,373,169 ops/sec ±0.36% (94 runs sampled) 76 | Fastest is EventEmitter3(master) 77 | ``` 78 | 79 | Starting benchmark hundreds.js 80 | 81 | ``` 82 | EventEmitter1 x 376,475 ops/sec ±0.32% (89 runs sampled) 83 | EventEmitter2 x 163,023 ops/sec ±0.30% (94 runs sampled) 84 | EventEmitter3@0.1.6 x 342,899 ops/sec ±0.32% (93 runs sampled) 85 | EventEmitter3(master) x 343,984 ops/sec ±0.31% (93 runs sampled) 86 | Drip x 323,076 ops/sec ±0.29% (90 runs sampled) 87 | fastemitter x 435,626 ops/sec ±0.32% (90 runs sampled) 88 | event-emitter x 169,640 ops/sec ±0.40% (90 runs sampled) 89 | contra/emitter x 94,023 ops/sec ±0.45% (90 runs sampled) 90 | Fastest is fastemitter 91 | ``` 92 | 93 | Starting benchmark emit.js 94 | 95 | ``` 96 | EventEmitter1 x 6,174,178 ops/sec ±0.28% (93 runs sampled) 97 | EventEmitter2 x 6,334,281 ops/sec ±0.72% (94 runs sampled) 98 | EventEmitter3@0.1.6 x 10,379,672 ops/sec ±0.38% (90 runs sampled) 99 | EventEmitter3(master) x 12,147,337 ops/sec ±0.54% (84 runs sampled) 100 | Drip x 6,290,746 ops/sec ±0.30% (93 runs sampled) 101 | fastemitter x 5,301,900 ops/sec ±0.72% (92 runs sampled) 102 | event-emitter x 4,887,035 ops/sec ±0.43% (89 runs sampled) 103 | contra/emitter x 427,013 ops/sec ±0.76% (93 runs sampled) 104 | Fastest is EventEmitter3(master) 105 | ``` 106 | 107 | Starting benchmark add-remove.js 108 | 109 | ``` 110 | EventEmitter1 x 4,737,119 ops/sec ±0.32% (91 runs sampled) 111 | EventEmitter2 x 4,775,450 ops/sec ±0.56% (88 runs sampled) 112 | EventEmitter3@0.1.6 x 7,598,665 ops/sec ±0.52% (91 runs sampled) 113 | EventEmitter3(master) x 18,016,994 ops/sec ±0.43% (87 runs sampled) 114 | Drip x 32,917,832 ops/sec ±1.55% (82 runs sampled) 115 | fastemitter x 15,904,236 ops/sec ±0.40% (88 runs sampled) 116 | event-emitter x 3,809,036 ops/sec ±0.75% (93 runs sampled) 117 | contra/emitter x 6,141,581 ops/sec ±0.33% (93 runs sampled) 118 | Fastest is Drip 119 | ``` 120 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var has = Object.prototype.hasOwnProperty 4 | , prefix = '~'; 5 | 6 | /** 7 | * Constructor to create a storage for our `EE` objects. 8 | * An `Events` instance is a plain object whose properties are event names. 9 | * 10 | * @constructor 11 | * @private 12 | */ 13 | function Events() {} 14 | 15 | // 16 | // We try to not inherit from `Object.prototype`. In some engines creating an 17 | // instance in this way is faster than calling `Object.create(null)` directly. 18 | // If `Object.create(null)` is not supported we prefix the event names with a 19 | // character to make sure that the built-in object properties are not 20 | // overridden or used as an attack vector. 21 | // 22 | if (Object.create) { 23 | Events.prototype = Object.create(null); 24 | 25 | // 26 | // This hack is needed because the `__proto__` property is still inherited in 27 | // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. 28 | // 29 | if (!new Events().__proto__) prefix = false; 30 | } 31 | 32 | /** 33 | * Representation of a single event listener. 34 | * 35 | * @param {Function} fn The listener function. 36 | * @param {*} context The context to invoke the listener with. 37 | * @param {Boolean} [once=false] Specify if the listener is a one-time listener. 38 | * @constructor 39 | * @private 40 | */ 41 | function EE(fn, context, once) { 42 | this.fn = fn; 43 | this.context = context; 44 | this.once = once || false; 45 | } 46 | 47 | /** 48 | * Add a listener for a given event. 49 | * 50 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 51 | * @param {(String|Symbol)} event The event name. 52 | * @param {Function} fn The listener function. 53 | * @param {*} context The context to invoke the listener with. 54 | * @param {Boolean} once Specify if the listener is a one-time listener. 55 | * @returns {EventEmitter} 56 | * @private 57 | */ 58 | function addListener(emitter, event, fn, context, once) { 59 | if (typeof fn !== 'function') { 60 | throw new TypeError('The listener must be a function'); 61 | } 62 | 63 | var listener = new EE(fn, context || emitter, once) 64 | , evt = prefix ? prefix + event : event; 65 | 66 | if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; 67 | else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); 68 | else emitter._events[evt] = [emitter._events[evt], listener]; 69 | 70 | return emitter; 71 | } 72 | 73 | /** 74 | * Clear event by name. 75 | * 76 | * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 77 | * @param {(String|Symbol)} evt The Event name. 78 | * @private 79 | */ 80 | function clearEvent(emitter, evt) { 81 | if (--emitter._eventsCount === 0) emitter._events = new Events(); 82 | else delete emitter._events[evt]; 83 | } 84 | 85 | /** 86 | * Minimal `EventEmitter` interface that is molded against the Node.js 87 | * `EventEmitter` interface. 88 | * 89 | * @constructor 90 | * @public 91 | */ 92 | function EventEmitter() { 93 | this._events = new Events(); 94 | this._eventsCount = 0; 95 | } 96 | 97 | /** 98 | * Return an array listing the events for which the emitter has registered 99 | * listeners. 100 | * 101 | * @returns {Array} 102 | * @public 103 | */ 104 | EventEmitter.prototype.eventNames = function eventNames() { 105 | var names = [] 106 | , events 107 | , name; 108 | 109 | if (this._eventsCount === 0) return names; 110 | 111 | for (name in (events = this._events)) { 112 | if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); 113 | } 114 | 115 | if (Object.getOwnPropertySymbols) { 116 | return names.concat(Object.getOwnPropertySymbols(events)); 117 | } 118 | 119 | return names; 120 | }; 121 | 122 | /** 123 | * Return the listeners registered for a given event. 124 | * 125 | * @param {(String|Symbol)} event The event name. 126 | * @returns {Array} The registered listeners. 127 | * @public 128 | */ 129 | EventEmitter.prototype.listeners = function listeners(event) { 130 | var evt = prefix ? prefix + event : event 131 | , handlers = this._events[evt]; 132 | 133 | if (!handlers) return []; 134 | if (handlers.fn) return [handlers.fn]; 135 | 136 | for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { 137 | ee[i] = handlers[i].fn; 138 | } 139 | 140 | return ee; 141 | }; 142 | 143 | /** 144 | * Return the number of listeners listening to a given event. 145 | * 146 | * @param {(String|Symbol)} event The event name. 147 | * @returns {Number} The number of listeners. 148 | * @public 149 | */ 150 | EventEmitter.prototype.listenerCount = function listenerCount(event) { 151 | var evt = prefix ? prefix + event : event 152 | , listeners = this._events[evt]; 153 | 154 | if (!listeners) return 0; 155 | if (listeners.fn) return 1; 156 | return listeners.length; 157 | }; 158 | 159 | /** 160 | * Calls each of the listeners registered for a given event. 161 | * 162 | * @param {(String|Symbol)} event The event name. 163 | * @returns {Boolean} `true` if the event had listeners, else `false`. 164 | * @public 165 | */ 166 | EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { 167 | var evt = prefix ? prefix + event : event; 168 | 169 | if (!this._events[evt]) return false; 170 | 171 | var listeners = this._events[evt] 172 | , len = arguments.length 173 | , args 174 | , i; 175 | 176 | if (listeners.fn) { 177 | if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); 178 | 179 | switch (len) { 180 | case 1: return listeners.fn.call(listeners.context), true; 181 | case 2: return listeners.fn.call(listeners.context, a1), true; 182 | case 3: return listeners.fn.call(listeners.context, a1, a2), true; 183 | case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; 184 | case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; 185 | case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; 186 | } 187 | 188 | for (i = 1, args = new Array(len -1); i < len; i++) { 189 | args[i - 1] = arguments[i]; 190 | } 191 | 192 | listeners.fn.apply(listeners.context, args); 193 | } else { 194 | var length = listeners.length 195 | , j; 196 | 197 | for (i = 0; i < length; i++) { 198 | if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); 199 | 200 | switch (len) { 201 | case 1: listeners[i].fn.call(listeners[i].context); break; 202 | case 2: listeners[i].fn.call(listeners[i].context, a1); break; 203 | case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; 204 | case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; 205 | default: 206 | if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { 207 | args[j - 1] = arguments[j]; 208 | } 209 | 210 | listeners[i].fn.apply(listeners[i].context, args); 211 | } 212 | } 213 | } 214 | 215 | return true; 216 | }; 217 | 218 | /** 219 | * Add a listener for a given event. 220 | * 221 | * @param {(String|Symbol)} event The event name. 222 | * @param {Function} fn The listener function. 223 | * @param {*} [context=this] The context to invoke the listener with. 224 | * @returns {EventEmitter} `this`. 225 | * @public 226 | */ 227 | EventEmitter.prototype.on = function on(event, fn, context) { 228 | return addListener(this, event, fn, context, false); 229 | }; 230 | 231 | /** 232 | * Add a one-time listener for a given event. 233 | * 234 | * @param {(String|Symbol)} event The event name. 235 | * @param {Function} fn The listener function. 236 | * @param {*} [context=this] The context to invoke the listener with. 237 | * @returns {EventEmitter} `this`. 238 | * @public 239 | */ 240 | EventEmitter.prototype.once = function once(event, fn, context) { 241 | return addListener(this, event, fn, context, true); 242 | }; 243 | 244 | /** 245 | * Remove the listeners of a given event. 246 | * 247 | * @param {(String|Symbol)} event The event name. 248 | * @param {Function} fn Only remove the listeners that match this function. 249 | * @param {*} context Only remove the listeners that have this context. 250 | * @param {Boolean} once Only remove one-time listeners. 251 | * @returns {EventEmitter} `this`. 252 | * @public 253 | */ 254 | EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { 255 | var evt = prefix ? prefix + event : event; 256 | 257 | if (!this._events[evt]) return this; 258 | if (!fn) { 259 | clearEvent(this, evt); 260 | return this; 261 | } 262 | 263 | var listeners = this._events[evt]; 264 | 265 | if (listeners.fn) { 266 | if ( 267 | listeners.fn === fn && 268 | (!once || listeners.once) && 269 | (!context || listeners.context === context) 270 | ) { 271 | clearEvent(this, evt); 272 | } 273 | } else { 274 | for (var i = 0, events = [], length = listeners.length; i < length; i++) { 275 | if ( 276 | listeners[i].fn !== fn || 277 | (once && !listeners[i].once) || 278 | (context && listeners[i].context !== context) 279 | ) { 280 | events.push(listeners[i]); 281 | } 282 | } 283 | 284 | // 285 | // Reset the array, or remove it completely if we have no more listeners. 286 | // 287 | if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; 288 | else clearEvent(this, evt); 289 | } 290 | 291 | return this; 292 | }; 293 | 294 | /** 295 | * Remove all listeners, or those of the specified event. 296 | * 297 | * @param {(String|Symbol)} [event] The event name. 298 | * @returns {EventEmitter} `this`. 299 | * @public 300 | */ 301 | EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { 302 | var evt; 303 | 304 | if (event) { 305 | evt = prefix ? prefix + event : event; 306 | if (this._events[evt]) clearEvent(this, evt); 307 | } else { 308 | this._events = new Events(); 309 | this._eventsCount = 0; 310 | } 311 | 312 | return this; 313 | }; 314 | 315 | // 316 | // Alias methods names because people roll like that. 317 | // 318 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 319 | EventEmitter.prototype.addListener = EventEmitter.prototype.on; 320 | 321 | // 322 | // Expose the prefix. 323 | // 324 | EventEmitter.prefixed = prefix; 325 | 326 | // 327 | // Allow `EventEmitter` to be imported as module namespace. 328 | // 329 | EventEmitter.EventEmitter = EventEmitter; 330 | 331 | // 332 | // Expose the module. 333 | // 334 | if ('undefined' !== typeof module) { 335 | module.exports = EventEmitter; 336 | } 337 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | describe('EventEmitter', function tests() { 2 | 'use strict'; 3 | 4 | var EventEmitter = require('../') 5 | , assume = require('assume'); 6 | 7 | it('exposes a `prefixed` property', function () { 8 | assume(EventEmitter.prefixed).is.either([false, '~']); 9 | }); 10 | 11 | it('exposes a module namespace object', function() { 12 | assume(EventEmitter.EventEmitter).equals(EventEmitter); 13 | }); 14 | 15 | it('inherits when used with `require("util").inherits`', function () { 16 | function Beast() { 17 | EventEmitter.call(this); 18 | } 19 | 20 | require('util').inherits(Beast, EventEmitter); 21 | 22 | var moop = new Beast() 23 | , meap = new Beast(); 24 | 25 | assume(moop).is.instanceOf(Beast); 26 | assume(moop).is.instanceOf(EventEmitter); 27 | 28 | moop.listeners(); 29 | meap.listeners(); 30 | 31 | moop.on('data', function () { 32 | throw new Error('I should not emit'); 33 | }); 34 | 35 | meap.emit('data', 'rawr'); 36 | meap.removeListener('foo'); 37 | meap.removeAllListeners(); 38 | }); 39 | 40 | if ('undefined' !== typeof Symbol) it('works with ES6 symbols', function (next) { 41 | var e = new EventEmitter() 42 | , event = Symbol('cows') 43 | , unknown = Symbol('moo'); 44 | 45 | e.on(event, function foo(arg) { 46 | assume(e.listenerCount(unknown)).equals(0); 47 | assume(e.listeners(unknown)).deep.equals([]); 48 | assume(arg).equals('bar'); 49 | 50 | function bar(onced) { 51 | assume(e.listenerCount(unknown)).equals(0); 52 | assume(e.listeners(unknown)).deep.equals([]); 53 | assume(onced).equals('foo'); 54 | next(); 55 | } 56 | 57 | e.once(unknown, bar); 58 | 59 | assume(e.listenerCount(event)).equals(1); 60 | assume(e.listeners(event)).deep.equals([foo]); 61 | assume(e.listenerCount(unknown)).equals(1); 62 | assume(e.listeners(unknown)).deep.equals([bar]); 63 | 64 | e.removeListener(event); 65 | 66 | assume(e.listenerCount(event)).equals(0); 67 | assume(e.listeners(event)).deep.equals([]); 68 | assume(e.emit(unknown, 'foo')).equals(true); 69 | }); 70 | 71 | assume(e.emit(unknown, 'bar')).equals(false); 72 | assume(e.emit(event, 'bar')).equals(true); 73 | }); 74 | 75 | describe('EventEmitter#emit', function () { 76 | it('should return false when there are not events to emit', function () { 77 | var e = new EventEmitter(); 78 | 79 | assume(e.emit('foo')).equals(false); 80 | assume(e.emit('bar')).equals(false); 81 | }); 82 | 83 | it('emits with context', function (done) { 84 | var context = { bar: 'baz' } 85 | , e = new EventEmitter(); 86 | 87 | e.on('foo', function (bar) { 88 | assume(bar).equals('bar'); 89 | assume(this).equals(context); 90 | 91 | done(); 92 | }, context).emit('foo', 'bar'); 93 | }); 94 | 95 | it('emits with context, multiple arguments (force apply)', function (done) { 96 | var context = { bar: 'baz' } 97 | , e = new EventEmitter(); 98 | 99 | e.on('foo', function (bar) { 100 | assume(bar).equals('bar'); 101 | assume(this).equals(context); 102 | 103 | done(); 104 | }, context).emit('foo', 'bar', 1, 2, 3, 4, 5, 6, 7, 8, 9, 0); 105 | }); 106 | 107 | it('can emit the function with multiple arguments', function () { 108 | var e = new EventEmitter(); 109 | 110 | for (var i = 0; i < 100; i++) { 111 | (function (j) { 112 | for (var i = 0, args = []; i < j; i++) { 113 | args.push(j); 114 | } 115 | 116 | e.once('args', function () { 117 | assume(arguments.length).equals(args.length); 118 | }); 119 | 120 | e.emit.apply(e, ['args'].concat(args)); 121 | })(i); 122 | } 123 | }); 124 | 125 | it('can emit the function with multiple arguments, multiple listeners', function () { 126 | var e = new EventEmitter(); 127 | 128 | for (var i = 0; i < 100; i++) { 129 | (function (j) { 130 | for (var i = 0, args = []; i < j; i++) { 131 | args.push(j); 132 | } 133 | 134 | e.once('args', function () { 135 | assume(arguments.length).equals(args.length); 136 | }); 137 | 138 | e.once('args', function () { 139 | assume(arguments.length).equals(args.length); 140 | }); 141 | 142 | e.once('args', function () { 143 | assume(arguments.length).equals(args.length); 144 | }); 145 | 146 | e.once('args', function () { 147 | assume(arguments.length).equals(args.length); 148 | }); 149 | 150 | e.emit.apply(e, ['args'].concat(args)); 151 | })(i); 152 | } 153 | }); 154 | 155 | it('emits with context, multiple listeners (force loop)', function () { 156 | var e = new EventEmitter(); 157 | 158 | e.on('foo', function (bar) { 159 | assume(this).eqls({ foo: 'bar' }); 160 | assume(bar).equals('bar'); 161 | }, { foo: 'bar' }); 162 | 163 | e.on('foo', function (bar) { 164 | assume(this).eqls({ bar: 'baz' }); 165 | assume(bar).equals('bar'); 166 | }, { bar: 'baz' }); 167 | 168 | e.emit('foo', 'bar'); 169 | }); 170 | 171 | it('emits with different contexts', function () { 172 | var e = new EventEmitter() 173 | , pattern = ''; 174 | 175 | function writer() { 176 | pattern += this; 177 | } 178 | 179 | e.on('write', writer, 'foo'); 180 | e.on('write', writer, 'baz'); 181 | e.once('write', writer, 'bar'); 182 | e.once('write', writer, 'banana'); 183 | 184 | e.emit('write'); 185 | assume(pattern).equals('foobazbarbanana'); 186 | }); 187 | 188 | it('should return true when there are events to emit', function () { 189 | var e = new EventEmitter() 190 | , called = 0; 191 | 192 | e.on('foo', function () { 193 | called++; 194 | }); 195 | 196 | assume(e.emit('foo')).equals(true); 197 | assume(e.emit('foob')).equals(false); 198 | assume(called).equals(1); 199 | }); 200 | 201 | it('receives the emitted events', function (done) { 202 | var e = new EventEmitter(); 203 | 204 | e.on('data', function (a, b, c, d, undef) { 205 | assume(a).equals('foo'); 206 | assume(b).equals(e); 207 | assume(c).is.instanceOf(Date); 208 | assume(undef).equals(undefined); 209 | assume(arguments.length).equals(3); 210 | 211 | done(); 212 | }); 213 | 214 | e.emit('data', 'foo', e, new Date()); 215 | }); 216 | 217 | it('emits to all event listeners', function () { 218 | var e = new EventEmitter() 219 | , pattern = []; 220 | 221 | e.on('foo', function () { 222 | pattern.push('foo1'); 223 | }); 224 | 225 | e.on('foo', function () { 226 | pattern.push('foo2'); 227 | }); 228 | 229 | e.emit('foo'); 230 | 231 | assume(pattern.join(';')).equals('foo1;foo2'); 232 | }); 233 | 234 | (function each(keys) { 235 | var key = keys.shift(); 236 | 237 | if (!key) return; 238 | 239 | it('can store event which is a known property: '+ key, function (next) { 240 | var e = new EventEmitter(); 241 | 242 | e.on(key, function (k) { 243 | assume(k).equals(key); 244 | next(); 245 | }).emit(key, key); 246 | }); 247 | 248 | each(keys); 249 | })([ 250 | 'hasOwnProperty', 251 | 'constructor', 252 | '__proto__', 253 | 'toString', 254 | 'toValue', 255 | 'unwatch', 256 | 'watch' 257 | ]); 258 | }); 259 | 260 | describe('EventEmitter#listeners', function () { 261 | it('returns an empty array if no listeners are specified', function () { 262 | var e = new EventEmitter(); 263 | 264 | assume(e.listeners('foo')).is.a('array'); 265 | assume(e.listeners('foo').length).equals(0); 266 | }); 267 | 268 | it('returns an array of function', function () { 269 | var e = new EventEmitter(); 270 | 271 | function foo() {} 272 | 273 | e.on('foo', foo); 274 | assume(e.listeners('foo')).is.a('array'); 275 | assume(e.listeners('foo').length).equals(1); 276 | assume(e.listeners('foo')).deep.equals([foo]); 277 | }); 278 | 279 | it('is not vulnerable to modifications', function () { 280 | var e = new EventEmitter(); 281 | 282 | function foo() {} 283 | 284 | e.on('foo', foo); 285 | 286 | assume(e.listeners('foo')).deep.equals([foo]); 287 | 288 | e.listeners('foo').length = 0; 289 | assume(e.listeners('foo')).deep.equals([foo]); 290 | }); 291 | }); 292 | 293 | describe('EventEmitter#listenerCount', function () { 294 | it('returns the number of listeners for a given event', function () { 295 | var e = new EventEmitter(); 296 | 297 | assume(e.listenerCount()).equals(0); 298 | assume(e.listenerCount('foo')).equals(0); 299 | 300 | e.on('foo', function () {}); 301 | assume(e.listenerCount('foo')).equals(1); 302 | e.on('foo', function () {}); 303 | assume(e.listenerCount('foo')).equals(2); 304 | }); 305 | }); 306 | 307 | describe('EventEmitter#on', function () { 308 | it('throws an error if the listener is not a function', function () { 309 | var e = new EventEmitter(); 310 | 311 | try { 312 | e.on('foo', 'bar'); 313 | } catch (ex) { 314 | assume(ex).is.instanceOf(TypeError); 315 | assume(ex.message).equals('The listener must be a function'); 316 | return; 317 | } 318 | 319 | throw new Error('oops'); 320 | }); 321 | }); 322 | 323 | describe('EventEmitter#once', function () { 324 | it('only emits it once', function () { 325 | var e = new EventEmitter() 326 | , calls = 0; 327 | 328 | e.once('foo', function () { 329 | calls++; 330 | }); 331 | 332 | e.emit('foo'); 333 | e.emit('foo'); 334 | e.emit('foo'); 335 | e.emit('foo'); 336 | e.emit('foo'); 337 | 338 | assume(e.listeners('foo').length).equals(0); 339 | assume(calls).equals(1); 340 | }); 341 | 342 | it('only emits once if emits are nested inside the listener', function () { 343 | var e = new EventEmitter() 344 | , calls = 0; 345 | 346 | e.once('foo', function () { 347 | calls++; 348 | e.emit('foo'); 349 | }); 350 | 351 | e.emit('foo'); 352 | assume(e.listeners('foo').length).equals(0); 353 | assume(calls).equals(1); 354 | }); 355 | 356 | it('only emits once for multiple events', function () { 357 | var e = new EventEmitter() 358 | , multi = 0 359 | , foo = 0 360 | , bar = 0; 361 | 362 | e.once('foo', function () { 363 | foo++; 364 | }); 365 | 366 | e.once('foo', function () { 367 | bar++; 368 | }); 369 | 370 | e.on('foo', function () { 371 | multi++; 372 | }); 373 | 374 | e.emit('foo'); 375 | e.emit('foo'); 376 | e.emit('foo'); 377 | e.emit('foo'); 378 | e.emit('foo'); 379 | 380 | assume(e.listeners('foo').length).equals(1); 381 | assume(multi).equals(5); 382 | assume(foo).equals(1); 383 | assume(bar).equals(1); 384 | }); 385 | 386 | it('only emits once with context', function (done) { 387 | var context = { foo: 'bar' } 388 | , e = new EventEmitter(); 389 | 390 | e.once('foo', function (bar) { 391 | assume(this).equals(context); 392 | assume(bar).equals('bar'); 393 | 394 | done(); 395 | }, context).emit('foo', 'bar'); 396 | }); 397 | }); 398 | 399 | describe('EventEmitter#removeListener', function () { 400 | it('removes all listeners when the listener is not specified', function () { 401 | var e = new EventEmitter(); 402 | 403 | e.on('foo', function () {}); 404 | e.on('foo', function () {}); 405 | 406 | assume(e.removeListener('foo')).equals(e); 407 | assume(e.listeners('foo')).eql([]); 408 | }); 409 | 410 | it('removes only the listeners matching the specified listener', function () { 411 | var e = new EventEmitter(); 412 | 413 | function foo() {} 414 | function bar() {} 415 | function baz() {} 416 | 417 | e.on('foo', foo); 418 | e.on('bar', bar); 419 | e.on('bar', baz); 420 | 421 | assume(e.removeListener('foo', bar)).equals(e); 422 | assume(e.listeners('bar')).eql([bar, baz]); 423 | assume(e.listeners('foo')).eql([foo]); 424 | assume(e._eventsCount).equals(2); 425 | 426 | assume(e.removeListener('foo', foo)).equals(e); 427 | assume(e.listeners('bar')).eql([bar, baz]); 428 | assume(e.listeners('foo')).eql([]); 429 | assume(e._eventsCount).equals(1); 430 | 431 | assume(e.removeListener('bar', bar)).equals(e); 432 | assume(e.listeners('bar')).eql([baz]); 433 | assume(e._eventsCount).equals(1); 434 | 435 | assume(e.removeListener('bar', baz)).equals(e); 436 | assume(e.listeners('bar')).eql([]); 437 | assume(e._eventsCount).equals(0); 438 | 439 | e.on('foo', foo); 440 | e.on('foo', foo); 441 | e.on('bar', bar); 442 | 443 | assume(e.removeListener('foo', foo)).equals(e); 444 | assume(e.listeners('bar')).eql([bar]); 445 | assume(e.listeners('foo')).eql([]); 446 | assume(e._eventsCount).equals(1); 447 | }); 448 | 449 | it('removes only the once listeners when using the once flag', function () { 450 | var e = new EventEmitter(); 451 | 452 | function foo() {} 453 | 454 | e.on('foo', foo); 455 | 456 | assume(e.removeListener('foo', function () {}, undefined, true)).equals(e); 457 | assume(e.listeners('foo')).eql([foo]); 458 | assume(e._eventsCount).equals(1); 459 | 460 | assume(e.removeListener('foo', foo, undefined, true)).equals(e); 461 | assume(e.listeners('foo')).eql([foo]); 462 | assume(e._eventsCount).equals(1); 463 | 464 | assume(e.removeListener('foo', foo)).equals(e); 465 | assume(e.listeners('foo')).eql([]); 466 | assume(e._eventsCount).equals(0); 467 | 468 | e.once('foo', foo); 469 | e.on('foo', foo); 470 | 471 | assume(e.removeListener('foo', function () {}, undefined, true)).equals(e); 472 | assume(e.listeners('foo')).eql([foo, foo]); 473 | assume(e._eventsCount).equals(1); 474 | 475 | assume(e.removeListener('foo', foo, undefined, true)).equals(e); 476 | assume(e.listeners('foo')).eql([foo]); 477 | assume(e._eventsCount).equals(1); 478 | 479 | e.once('foo', foo); 480 | 481 | assume(e.removeListener('foo', foo)).equals(e); 482 | assume(e.listeners('foo')).eql([]); 483 | assume(e._eventsCount).equals(0); 484 | }); 485 | 486 | it('removes only the listeners matching the correct context', function () { 487 | var context = { foo: 'bar' } 488 | , e = new EventEmitter(); 489 | 490 | function foo() {} 491 | function bar() {} 492 | 493 | e.on('foo', foo, context); 494 | 495 | assume(e.removeListener('foo', function () {}, context)).equals(e); 496 | assume(e.listeners('foo')).eql([foo]); 497 | assume(e._eventsCount).equals(1); 498 | 499 | assume(e.removeListener('foo', foo, { baz: 'quux' })).equals(e); 500 | assume(e.listeners('foo')).eql([foo]); 501 | assume(e._eventsCount).equals(1); 502 | 503 | assume(e.removeListener('foo', foo, context)).equals(e); 504 | assume(e.listeners('foo')).eql([]); 505 | assume(e._eventsCount).equals(0); 506 | 507 | e.on('foo', foo, context); 508 | e.on('foo', bar); 509 | 510 | assume(e.removeListener('foo', foo, { baz: 'quux' })).equals(e); 511 | assume(e.listeners('foo')).eql([foo, bar]); 512 | assume(e._eventsCount).equals(1); 513 | 514 | assume(e.removeListener('foo', foo, context)).equals(e); 515 | assume(e.listeners('foo')).eql([bar]); 516 | assume(e._eventsCount).equals(1); 517 | 518 | e.on('foo', bar, context); 519 | 520 | assume(e.removeListener('foo', bar)).equals(e); 521 | assume(e.listeners('foo')).eql([]); 522 | assume(e._eventsCount).equals(0); 523 | }); 524 | }); 525 | 526 | describe('EventEmitter#removeAllListeners', function () { 527 | it('removes all events for the specified events', function () { 528 | var e = new EventEmitter(); 529 | 530 | e.on('foo', function () { throw new Error('oops'); }); 531 | e.on('foo', function () { throw new Error('oops'); }); 532 | e.on('bar', function () { throw new Error('oops'); }); 533 | e.on('aaa', function () { throw new Error('oops'); }); 534 | 535 | assume(e.removeAllListeners('foo')).equals(e); 536 | assume(e.listeners('foo').length).equals(0); 537 | assume(e.listeners('bar').length).equals(1); 538 | assume(e.listeners('aaa').length).equals(1); 539 | assume(e._eventsCount).equals(2); 540 | 541 | assume(e.removeAllListeners('bar')).equals(e); 542 | assume(e._eventsCount).equals(1); 543 | assume(e.removeAllListeners('aaa')).equals(e); 544 | assume(e._eventsCount).equals(0); 545 | 546 | assume(e.emit('foo')).equals(false); 547 | assume(e.emit('bar')).equals(false); 548 | assume(e.emit('aaa')).equals(false); 549 | }); 550 | 551 | it('just nukes the fuck out of everything', function () { 552 | var e = new EventEmitter(); 553 | 554 | e.on('foo', function () { throw new Error('oops'); }); 555 | e.on('foo', function () { throw new Error('oops'); }); 556 | e.on('bar', function () { throw new Error('oops'); }); 557 | e.on('aaa', function () { throw new Error('oops'); }); 558 | 559 | assume(e.removeAllListeners()).equals(e); 560 | assume(e.listeners('foo').length).equals(0); 561 | assume(e.listeners('bar').length).equals(0); 562 | assume(e.listeners('aaa').length).equals(0); 563 | assume(e._eventsCount).equals(0); 564 | 565 | assume(e.emit('foo')).equals(false); 566 | assume(e.emit('bar')).equals(false); 567 | assume(e.emit('aaa')).equals(false); 568 | }); 569 | }); 570 | 571 | describe('EventEmitter#eventNames', function () { 572 | it('returns an empty array when there are no events', function () { 573 | var e = new EventEmitter(); 574 | 575 | assume(e.eventNames()).eql([]); 576 | 577 | e.on('foo', function () {}); 578 | e.removeAllListeners('foo'); 579 | 580 | assume(e.eventNames()).eql([]); 581 | }); 582 | 583 | it('returns an array listing the events that have listeners', function () { 584 | var e = new EventEmitter() 585 | , original; 586 | 587 | function bar() {} 588 | 589 | if (Object.getOwnPropertySymbols) { 590 | // 591 | // Monkey patch `Object.getOwnPropertySymbols()` to increase coverage 592 | // on Node.js > 0.10. 593 | // 594 | original = Object.getOwnPropertySymbols; 595 | Object.getOwnPropertySymbols = undefined; 596 | } 597 | 598 | e.on('foo', function () {}); 599 | e.on('bar', bar); 600 | 601 | try { 602 | assume(e.eventNames()).eql(['foo', 'bar']); 603 | e.removeListener('bar', bar); 604 | assume(e.eventNames()).eql(['foo']); 605 | } catch (ex) { 606 | throw ex; 607 | } finally { 608 | if (original) Object.getOwnPropertySymbols = original; 609 | } 610 | }); 611 | 612 | it('does not return inherited property identifiers', function () { 613 | var e = new EventEmitter(); 614 | 615 | function Collection() {} 616 | Collection.prototype.foo = function () { 617 | return 'foo'; 618 | }; 619 | 620 | e._events = new Collection(); 621 | 622 | assume(e._events.foo()).equal('foo'); 623 | assume(e.eventNames()).eql([]); 624 | }); 625 | 626 | if ('undefined' !== typeof Symbol) it('includes ES6 symbols', function () { 627 | var e = new EventEmitter() 628 | , s = Symbol('s'); 629 | 630 | function foo() {} 631 | 632 | e.on('foo', foo); 633 | e.on(s, function () {}); 634 | 635 | assume(e.eventNames()).eql(['foo', s]); 636 | 637 | e.removeListener('foo', foo); 638 | 639 | assume(e.eventNames()).eql([s]); 640 | }); 641 | }); 642 | }); 643 | --------------------------------------------------------------------------------