| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 0f0f4e0 commit aa1cb3f
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -623,41 +623,20 @@ function unwrapListeners(arr) { | |||
| 623 | 623 | ||
| 624 | 624 | function once(emitter, name) { | |
| 625 | 625 | return new Promise((resolve, reject) => { | |
| 626 | - if (typeof emitter.addEventListener === 'function') { | ||
| 627 | - // EventTarget does not have `error` event semantics like Node | ||
| 628 | - // EventEmitters, we do not listen to `error` events here. | ||
| 629 | - emitter.addEventListener( | ||
| 630 | - name, | ||
| 631 | - (...args) => { resolve(args); }, | ||
| 632 | - { once: true } | ||
| 633 | - ); | ||
| 634 | - return; | ||
| 635 | - } | ||
| 636 | - | ||
| 637 | - const eventListener = (...args) => { | ||
| 638 | - if (errorListener !== undefined) { | ||
| 626 | + const errorListener = (err) => { | ||
| 627 | + emitter.removeListener(name, resolver); | ||
| 628 | + reject(err); | ||
| 629 | + }; | ||
| 630 | + const resolver = (...args) => { | ||
| 631 | + if (typeof emitter.removeListener === 'function') { | ||
| 639 | 632 | emitter.removeListener('error', errorListener); | |
| 640 | 633 | } | |
| 641 | 634 | resolve(args); | |
| 642 | 635 | }; | |
| 643 | - let errorListener; | ||
| 644 | - | ||
| 645 | - // Adding an error listener is not optional because | ||
| 646 | - // if an error is thrown on an event emitter we cannot | ||
| 647 | - // guarantee that the actual event we are waiting will | ||
| 648 | - // be fired. The result could be a silent way to create | ||
| 649 | - // memory or file descriptor leaks, which is something | ||
| 650 | - // we should avoid. | ||
| 636 | + eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); | ||
| 651 | 637 | if (name !== 'error') { | |
| 652 | - errorListener = (err) => { | ||
| 653 | - emitter.removeListener(name, eventListener); | ||
| 654 | - reject(err); | ||
| 655 | - }; | ||
| 656 | - | ||
| 657 | - emitter.once('error', errorListener); | ||
| 638 | + addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); | ||
| 658 | 639 | } | |
| 659 | - | ||
| 660 | - emitter.once(name, eventListener); | ||
| 661 | 640 | }); | |
| 662 | 641 | } | |
| 663 | 642 | ||
@@ -668,6 +647,38 @@ function createIterResult(value, done) { | |||
| 668 | 647 | return { value, done }; | |
| 669 | 648 | } | |
| 670 | 649 | ||
| 650 | + function addErrorHandlerIfEventEmitter(emitter, handler, flags) { | ||
| 651 | + if (typeof emitter.on === 'function') { | ||
| 652 | + eventTargetAgnosticAddListener(emitter, 'error', handler, flags); | ||
| 653 | + } | ||
| 654 | + } | ||
| 655 | + | ||
| 656 | + function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) { | ||
| 657 | + if (typeof emitter.removeListener === 'function') { | ||
| 658 | + emitter.removeListener(name, listener); | ||
| 659 | + } else if (typeof emitter.removeEventListener === 'function') { | ||
| 660 | + emitter.removeEventListener(name, listener, flags); | ||
| 661 | + } else { | ||
| 662 | + throw new ERR_INVALID_ARG_TYPE('emitter', 'EventEmitter', emitter); | ||
| 663 | + } | ||
| 664 | + } | ||
| 665 | + | ||
| 666 | + function eventTargetAgnosticAddListener(emitter, name, listener, flags) { | ||
| 667 | + if (typeof emitter.on === 'function') { | ||
| 668 | + if (flags && flags.once) { | ||
| 669 | + emitter.once(name, listener); | ||
| 670 | + } else { | ||
| 671 | + emitter.on(name, listener); | ||
| 672 | + } | ||
| 673 | + } else if (typeof emitter.addEventListener === 'function') { | ||
| 674 | + // EventTarget does not have `error` event semantics like Node | ||
| 675 | + // EventEmitters, we do not listen to `error` events here. | ||
| 676 | + emitter.addEventListener(name, (arg) => { listener(arg); }, flags); | ||
| 677 | + } else { | ||
| 678 | + throw new ERR_INVALID_ARG_TYPE('emitter', 'EventEmitter', emitter); | ||
| 679 | + } | ||
| 680 | + } | ||
| 681 | + | ||
| 671 | 682 | function on(emitter, event) { | |
| 672 | 683 | const unconsumedEvents = []; | |
| 673 | 684 | const unconsumedPromises = []; | |
@@ -704,8 +715,8 @@ function on(emitter, event) { | |||
| 704 | 715 | }, | |
| 705 | 716 | ||
| 706 | 717 | return() { | |
| 707 | - emitter.removeListener(event, eventHandler); | ||
| 708 | - emitter.removeListener('error', errorHandler); | ||
| 718 | + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); | ||
| 719 | + eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler); | ||
| 709 | 720 | finished = true; | |
| 710 | 721 | ||
| 711 | 722 | for (const promise of unconsumedPromises) { | |
@@ -721,17 +732,20 @@ function on(emitter, event) { | |||
| 721 | 732 | 'Error', err); | |
| 722 | 733 | } | |
| 723 | 734 | error = err; | |
| 724 | - emitter.removeListener(event, eventHandler); | ||
| 725 | - emitter.removeListener('error', errorHandler); | ||
| 735 | + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); | ||
| 736 | + eventTargetAgnosticRemoveListener(emitter, 'error', errorHandler); | ||
| 726 | 737 | }, | |
| 727 | 738 | ||
| 728 | 739 | [SymbolAsyncIterator]() { | |
| 729 | 740 | return this; | |
| 730 | 741 | } | |
| 731 | 742 | }, AsyncIteratorPrototype); | |
| 732 | 743 | ||
| 733 | - emitter.on(event, eventHandler); | ||
| 734 | - emitter.on('error', errorHandler); | ||
| 744 | + eventTargetAgnosticAddListener(emitter, event, eventHandler); | ||
| 745 | + if (event !== 'error') { | ||
| 746 | + addErrorHandlerIfEventEmitter(emitter, errorHandler); | ||
| 747 | + } | ||
| 748 | + | ||
| 735 | 749 | ||
| 736 | 750 | return iterator; | |
| 737 | 751 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,8 +1,14 @@ | |||
| 1 | + // Flags: --expose-internals | ||
| 1 | 2 | 'use strict'; | |
| 2 | 3 | ||
| 3 | 4 | const common = require('../common'); | |
| 4 | 5 | const assert = require('assert'); | |
| 5 | 6 | const { on, EventEmitter } = require('events'); | |
| 7 | + const { | ||
| 8 | + EventTarget, | ||
| 9 | + NodeEventTarget, | ||
| 10 | + Event | ||
| 11 | + } = require('internal/event_target'); | ||
| 6 | 12 | ||
| 7 | 13 | async function basic() { | |
| 8 | 14 | const ee = new EventEmitter(); | |
@@ -204,6 +210,45 @@ async function iterableThrow() { | |||
| 204 | 210 | assert.strictEqual(ee.listenerCount('error'), 0); | |
| 205 | 211 | } | |
| 206 | 212 | ||
| 213 | + async function eventTarget() { | ||
| 214 | + const et = new EventTarget(); | ||
| 215 | + const tick = () => et.dispatchEvent(new Event('tick')); | ||
| 216 | + const interval = setInterval(tick, 0); | ||
| 217 | + let count = 0; | ||
| 218 | + for await (const [ event ] of on(et, 'tick')) { | ||
| 219 | + count++; | ||
| 220 | + assert.strictEqual(event.type, 'tick'); | ||
| 221 | + if (count >= 5) { | ||
| 222 | + break; | ||
| 223 | + } | ||
| 224 | + } | ||
| 225 | + assert.strictEqual(count, 5); | ||
| 226 | + clearInterval(interval); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + async function errorListenerCount() { | ||
| 230 | + const et = new EventEmitter(); | ||
| 231 | + on(et, 'foo'); | ||
| 232 | + assert.strictEqual(et.listenerCount('error'), 1); | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + async function nodeEventTarget() { | ||
| 236 | + const et = new NodeEventTarget(); | ||
| 237 | + const tick = () => et.dispatchEvent(new Event('tick')); | ||
| 238 | + const interval = setInterval(tick, 0); | ||
| 239 | + let count = 0; | ||
| 240 | + for await (const [ event] of on(et, 'tick')) { | ||
| 241 | + count++; | ||
| 242 | + assert.strictEqual(event.type, 'tick'); | ||
| 243 | + if (count >= 5) { | ||
| 244 | + break; | ||
| 245 | + } | ||
| 246 | + } | ||
| 247 | + assert.strictEqual(count, 5); | ||
| 248 | + clearInterval(interval); | ||
| 249 | + } | ||
| 250 | + | ||
| 251 | + | ||
| 207 | 252 | async function run() { | |
| 208 | 253 | const funcs = [ | |
| 209 | 254 | basic, | |
@@ -212,7 +257,10 @@ async function run() { | |||
| 212 | 257 | throwInLoop, | |
| 213 | 258 | next, | |
| 214 | 259 | nextError, | |
| 215 | - iterableThrow | ||
| 260 | + iterableThrow, | ||
| 261 | + eventTarget, | ||
| 262 | + errorListenerCount, | ||
| 263 | + nodeEventTarget | ||
| 216 | 264 | ]; | |
| 217 | 265 | ||
| 218 | 266 | for (const fn of funcs) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,55 +1,10 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | + // Flags: --expose-internals | ||
| 2 | 3 | ||
| 3 | 4 | const common = require('../common'); | |
| 4 | 5 | const { once, EventEmitter } = require('events'); | |
| 5 | - const { strictEqual, deepStrictEqual } = require('assert'); | ||
| 6 | - | ||
| 7 | - class EventTargetMock { | ||
| 8 | - constructor() { | ||
| 9 | - this.events = {}; | ||
| 10 | - } | ||
| 11 | - | ||
| 12 | - addEventListener = common.mustCall(function(name, listener, options) { | ||
| 13 | - if (!(name in this.events)) { | ||
| 14 | - this.events[name] = { listeners: [], options }; | ||
| 15 | - } | ||
| 16 | - this.events[name].listeners.push(listener); | ||
| 17 | - }); | ||
| 18 | - | ||
| 19 | - removeEventListener = common.mustCall(function(name, callback) { | ||
| 20 | - if (!(name in this.events)) { | ||
| 21 | - return; | ||
| 22 | - } | ||
| 23 | - const event = this.events[name]; | ||
| 24 | - const stack = event.listeners; | ||
| 25 | - | ||
| 26 | - for (let i = 0, l = stack.length; i < l; i++) { | ||
| 27 | - if (stack[i] === callback) { | ||
| 28 | - stack.splice(i, 1); | ||
| 29 | - if (stack.length === 0) { | ||
| 30 | - Reflect.deleteProperty(this.events, name); | ||
| 31 | - } | ||
| 32 | - return; | ||
| 33 | - } | ||
| 34 | - } | ||
| 35 | - }); | ||
| 36 | - | ||
| 37 | - dispatchEvent = function(name, ...arg) { | ||
| 38 | - if (!(name in this.events)) { | ||
| 39 | - return true; | ||
| 40 | - } | ||
| 41 | - const event = this.events[name]; | ||
| 42 | - const stack = event.listeners.slice(); | ||
| 43 | - | ||
| 44 | - for (let i = 0, l = stack.length; i < l; i++) { | ||
| 45 | - stack[i].apply(this, arg); | ||
| 46 | - if (event.options.once) { | ||
| 47 | - this.removeEventListener(name, stack[i]); | ||
| 48 | - } | ||
| 49 | - } | ||
| 50 | - return !name.defaultPrevented; | ||
| 51 | - }; | ||
| 52 | - } | ||
| 6 | + const { strictEqual, deepStrictEqual, fail } = require('assert'); | ||
| 7 | + const { EventTarget, Event } = require('internal/event_target'); | ||
| 53 | 8 | ||
| 54 | 9 | async function onceAnEvent() { | |
| 55 | 10 | const ee = new EventEmitter(); | |
@@ -104,8 +59,6 @@ async function stopListeningAfterCatchingError() { | |||
| 104 | 59 | ee.emit('myevent', 42, 24); | |
| 105 | 60 | }); | |
| 106 | 61 | ||
| 107 | - process.on('multipleResolves', common.mustNotCall()); | ||
| 108 | - | ||
| 109 | 62 | try { | |
| 110 | 63 | await once(ee, 'myevent'); | |
| 111 | 64 | } catch (_e) { | |
@@ -125,54 +78,49 @@ async function onceError() { | |||
| 125 | 78 | ee.emit('error', expected); | |
| 126 | 79 | }); | |
| 127 | 80 | ||
| 128 | - const [err] = await once(ee, 'error'); | ||
| 81 | + const promise = once(ee, 'error'); | ||
| 82 | + strictEqual(ee.listenerCount('error'), 1); | ||
| 83 | + const [ err ] = await promise; | ||
| 129 | 84 | strictEqual(err, expected); | |
| 130 | 85 | strictEqual(ee.listenerCount('error'), 0); | |
| 131 | 86 | strictEqual(ee.listenerCount('myevent'), 0); | |
| 132 | 87 | } | |
| 133 | 88 | ||
| 134 | 89 | async function onceWithEventTarget() { | |
| 135 | - const et = new EventTargetMock(); | ||
| 136 | - | ||
| 90 | + const et = new EventTarget(); | ||
| 91 | + const event = new Event('myevent'); | ||
| 137 | 92 | process.nextTick(() => { | |
| 138 | - et.dispatchEvent('myevent', 42); | ||
| 93 | + et.dispatchEvent(event); | ||
| 139 | 94 | }); | |
| 140 | 95 | const [ value ] = await once(et, 'myevent'); | |
| 141 | - strictEqual(value, 42); | ||
| 142 | - strictEqual(Reflect.has(et.events, 'myevent'), false); | ||
| 143 | - } | ||
| 144 | - | ||
| 145 | - async function onceWithEventTargetTwoArgs() { | ||
| 146 | - const et = new EventTargetMock(); | ||
| 147 | - | ||
| 148 | - process.nextTick(() => { | ||
| 149 | - et.dispatchEvent('myevent', 42, 24); | ||
| 150 | - }); | ||
| 151 | - | ||
| 152 | - const value = await once(et, 'myevent'); | ||
| 153 | - deepStrictEqual(value, [42, 24]); | ||
| 96 | + strictEqual(value, event); | ||
| 154 | 97 | } | |
| 155 | 98 | ||
| 156 | 99 | async function onceWithEventTargetError() { | |
| 157 | - const et = new EventTargetMock(); | ||
| 158 | - | ||
| 159 | - const expected = new Error('kaboom'); | ||
| 100 | + const et = new EventTarget(); | ||
| 101 | + const error = new Event('error'); | ||
| 160 | 102 | process.nextTick(() => { | |
| 161 | - et.dispatchEvent('error', expected); | ||
| 103 | + et.dispatchEvent(error); | ||
| 162 | 104 | }); | |
| 163 | 105 | ||
| 164 | - const [err] = await once(et, 'error'); | ||
| 165 | - strictEqual(err, expected); | ||
| 166 | - strictEqual(Reflect.has(et.events, 'error'), false); | ||
| 106 | + const [ err ] = await once(et, 'error'); | ||
| 107 | + strictEqual(err, error); | ||
| 167 | 108 | } | |
| 168 | 109 | ||
| 110 | + async function prioritizesEventEmitter() { | ||
| 111 | + const ee = new EventEmitter(); | ||
| 112 | + ee.addEventListener = fail; | ||
| 113 | + ee.removeAllListeners = fail; | ||
| 114 | + process.nextTick(() => ee.emit('foo')); | ||
| 115 | + await once(ee, 'foo'); | ||
| 116 | + } | ||
| 169 | 117 | Promise.all([ | |
| 170 | 118 | onceAnEvent(), | |
| 171 | 119 | onceAnEventWithTwoArgs(), | |
| 172 | 120 | catchesErrors(), | |
| 173 | 121 | stopListeningAfterCatchingError(), | |
| 174 | 122 | onceError(), | |
| 175 | 123 | onceWithEventTarget(), | |
| 176 | - onceWithEventTargetTwoArgs(), | ||
| 177 | 124 | onceWithEventTargetError(), | |
| 125 | + prioritizesEventEmitter(), | ||
| 178 | 126 | ]).then(common.mustCall()); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments