events
Advanced tools
Comparing version 3.2.0 to 3.3.0
@@ -451,4 +451,9 @@ // Copyright Joyent, Inc. and other Node contributors. | ||
return new Promise(function (resolve, reject) { | ||
function eventListener() { | ||
if (errorListener !== undefined) { | ||
function errorListener(err) { | ||
emitter.removeListener(name, resolver); | ||
reject(err); | ||
} | ||
function resolver() { | ||
if (typeof emitter.removeListener === 'function') { | ||
emitter.removeListener('error', errorListener); | ||
@@ -458,21 +463,37 @@ } | ||
}; | ||
var errorListener; | ||
// Adding an error listener is not optional because | ||
// if an error is thrown on an event emitter we cannot | ||
// guarantee that the actual event we are waiting will | ||
// be fired. The result could be a silent way to create | ||
// memory or file descriptor leaks, which is something | ||
// we should avoid. | ||
eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); | ||
if (name !== 'error') { | ||
errorListener = function errorListener(err) { | ||
emitter.removeListener(name, eventListener); | ||
reject(err); | ||
}; | ||
emitter.once('error', errorListener); | ||
addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true }); | ||
} | ||
emitter.once(name, eventListener); | ||
}); | ||
} | ||
function addErrorHandlerIfEventEmitter(emitter, handler, flags) { | ||
if (typeof emitter.on === 'function') { | ||
eventTargetAgnosticAddListener(emitter, 'error', handler, flags); | ||
} | ||
} | ||
function eventTargetAgnosticAddListener(emitter, name, listener, flags) { | ||
if (typeof emitter.on === 'function') { | ||
if (flags.once) { | ||
emitter.once(name, listener); | ||
} else { | ||
emitter.on(name, listener); | ||
} | ||
} else if (typeof emitter.addEventListener === 'function') { | ||
// EventTarget does not have `error` event semantics like Node | ||
// EventEmitters, we do not listen for `error` events here. | ||
emitter.addEventListener(name, function wrapListener(arg) { | ||
// IE does not have builtin `{ once: true }` support so we | ||
// have to do it manually. | ||
if (flags.once) { | ||
emitter.removeEventListener(name, wrapListener); | ||
} | ||
listener(arg); | ||
}); | ||
} else { | ||
throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter); | ||
} | ||
} |
@@ -0,1 +1,24 @@ | ||
# 3.3.0 | ||
- Support EventTarget emitters in `events.once` from Node.js 12.11.0. | ||
Now you can use the `events.once` function with objects that implement the EventTarget interface. This interface is used widely in | ||
the DOM and other web APIs. | ||
```js | ||
var events = require('events'); | ||
var assert = require('assert'); | ||
async function connect() { | ||
var ws = new WebSocket('wss://example.com'); | ||
await events.once(ws, 'open'); | ||
assert(ws.readyState === WebSocket.OPEN); | ||
} | ||
async function onClick() { | ||
await events.once(document.body, 'click'); | ||
alert('you clicked the page!'); | ||
} | ||
``` | ||
# 3.2.0 | ||
@@ -2,0 +25,0 @@ |
{ | ||
"name": "events", | ||
"version": "3.2.0", | ||
"version": "3.3.0", | ||
"description": "Node's event emitter for all engines.", | ||
@@ -27,2 +27,3 @@ "keywords": [ | ||
"functions-have-names": "^1.2.1", | ||
"has": "^1.0.3", | ||
"has-symbols": "^1.0.1", | ||
@@ -29,0 +30,0 @@ "isarray": "^2.0.5", |
@@ -49,2 +49,3 @@ # events [![Build Status](https://travis-ci.org/Gozala/events.png?branch=master)](https://travis-ci.org/Gozala/events) | ||
[MIT](./LICENSE) | ||
[node.js docs]: https://nodejs.org/dist/v11.13.0/docs/api/events.html |
@@ -6,4 +6,58 @@ 'use strict'; | ||
var once = require('../').once; | ||
var has = require('has'); | ||
var assert = require('assert'); | ||
function Event(type) { | ||
this.type = type; | ||
} | ||
function EventTargetMock() { | ||
this.events = {}; | ||
this.addEventListener = common.mustCall(this.addEventListener); | ||
this.removeEventListener = common.mustCall(this.removeEventListener); | ||
} | ||
EventTargetMock.prototype.addEventListener = function addEventListener(name, listener, options) { | ||
if (!(name in this.events)) { | ||
this.events[name] = { listeners: [], options: options || {} } | ||
} | ||
this.events[name].listeners.push(listener); | ||
}; | ||
EventTargetMock.prototype.removeEventListener = function removeEventListener(name, callback) { | ||
if (!(name in this.events)) { | ||
return; | ||
} | ||
var event = this.events[name]; | ||
var stack = event.listeners; | ||
for (var i = 0, l = stack.length; i < l; i++) { | ||
if (stack[i] === callback) { | ||
stack.splice(i, 1); | ||
if (stack.length === 0) { | ||
delete this.events[name]; | ||
} | ||
return; | ||
} | ||
} | ||
}; | ||
EventTargetMock.prototype.dispatchEvent = function dispatchEvent(arg) { | ||
if (!(arg.type in this.events)) { | ||
return true; | ||
} | ||
var event = this.events[arg.type]; | ||
var stack = event.listeners.slice(); | ||
for (var i = 0, l = stack.length; i < l; i++) { | ||
stack[i].call(null, arg); | ||
if (event.options.once) { | ||
this.removeEventListener(arg.type, stack[i]); | ||
} | ||
} | ||
return !arg.defaultPrevented; | ||
}; | ||
function onceAnEvent() { | ||
@@ -84,3 +138,5 @@ var ee = new EventEmitter(); | ||
return once(ee, 'error').then(function (args) { | ||
var promise = once(ee, 'error'); | ||
assert.strictEqual(ee.listenerCount('error'), 1); | ||
return promise.then(function (args) { | ||
var err = args[0] | ||
@@ -93,3 +149,39 @@ assert.strictEqual(err, expected); | ||
Promise.all([ | ||
function onceWithEventTarget() { | ||
var et = new EventTargetMock(); | ||
var event = new Event('myevent'); | ||
process.nextTick(function () { | ||
et.dispatchEvent(event); | ||
}); | ||
return once(et, 'myevent').then(function (args) { | ||
var value = args[0]; | ||
assert.strictEqual(value, event); | ||
assert.strictEqual(has(et.events, 'myevent'), false); | ||
}); | ||
} | ||
function onceWithEventTargetError() { | ||
var et = new EventTargetMock(); | ||
var error = new Event('error'); | ||
process.nextTick(function () { | ||
et.dispatchEvent(error); | ||
}); | ||
return once(et, 'error').then(function (args) { | ||
var err = args[0]; | ||
assert.strictEqual(err, error); | ||
assert.strictEqual(has(et.events, 'error'), false); | ||
}); | ||
} | ||
function prioritizesEventEmitter() { | ||
var ee = new EventEmitter(); | ||
ee.addEventListener = assert.fail; | ||
ee.removeAllListeners = assert.fail; | ||
process.nextTick(function () { | ||
ee.emit('foo'); | ||
}); | ||
return once(ee, 'foo'); | ||
} | ||
var allTests = [ | ||
onceAnEvent(), | ||
@@ -99,6 +191,48 @@ onceAnEventWithTwoArgs(), | ||
stopListeningAfterCatchingError(), | ||
onceError() | ||
]).catch(function (err) { | ||
console.error(err.stack) | ||
process.exit(1) | ||
}); | ||
onceError(), | ||
onceWithEventTarget(), | ||
onceWithEventTargetError(), | ||
prioritizesEventEmitter() | ||
]; | ||
var hasBrowserEventTarget = false; | ||
try { | ||
hasBrowserEventTarget = typeof (new window.EventTarget().addEventListener) === 'function' && | ||
new window.Event('xyz').type === 'xyz'; | ||
} catch (err) {} | ||
if (hasBrowserEventTarget) { | ||
var onceWithBrowserEventTarget = function onceWithBrowserEventTarget() { | ||
var et = new window.EventTarget(); | ||
var event = new window.Event('myevent'); | ||
process.nextTick(function () { | ||
et.dispatchEvent(event); | ||
}); | ||
return once(et, 'myevent').then(function (args) { | ||
var value = args[0]; | ||
assert.strictEqual(value, event); | ||
assert.strictEqual(has(et.events, 'myevent'), false); | ||
}); | ||
} | ||
var onceWithBrowserEventTargetError = function onceWithBrowserEventTargetError() { | ||
var et = new window.EventTarget(); | ||
var error = new window.Event('error'); | ||
process.nextTick(function () { | ||
et.dispatchEvent(error); | ||
}); | ||
return once(et, 'error').then(function (args) { | ||
var err = args[0]; | ||
assert.strictEqual(err, error); | ||
assert.strictEqual(has(et.events, 'error'), false); | ||
}); | ||
} | ||
common.test.comment('Testing with browser built-in EventTarget'); | ||
allTests.push([ | ||
onceWithBrowserEventTarget(), | ||
onceWithBrowserEventTargetError() | ||
]); | ||
} | ||
module.exports = Promise.all(allTests); |
@@ -17,3 +17,11 @@ var test = require('tape'); | ||
try { orig_require(file); } catch (err) { t.fail(err); } | ||
try { | ||
var exp = orig_require(file); | ||
if (exp && exp.then) { | ||
exp.then(function () { t.end(); }, t.fail); | ||
return; | ||
} | ||
} catch (err) { | ||
t.fail(err); | ||
} | ||
t.end(); | ||
@@ -20,0 +28,0 @@ }); |
82778
1951
51
6