aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/bun.js/bindings/headers-cpp.h2
-rw-r--r--src/bun.js/bindings/headers.h2
-rw-r--r--src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp189
-rw-r--r--src/bun.js/builtins/js/NodeEvents.js191
-rw-r--r--test/js/node/events/node-events.node.test.ts261
5 files changed, 276 insertions, 369 deletions
diff --git a/src/bun.js/bindings/headers-cpp.h b/src/bun.js/bindings/headers-cpp.h
index 06a42eb57..5eac6785d 100644
--- a/src/bun.js/bindings/headers-cpp.h
+++ b/src/bun.js/bindings/headers-cpp.h
@@ -1,4 +1,4 @@
-//-- AUTOGENERATED FILE -- 1680036484
+//-- AUTOGENERATED FILE -- 1680635796
// clang-format off
#pragma once
diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h
index a53938787..5b0e9527c 100644
--- a/src/bun.js/bindings/headers.h
+++ b/src/bun.js/bindings/headers.h
@@ -1,5 +1,5 @@
// clang-format off
-//-- AUTOGENERATED FILE -- 1680036484
+//-- AUTOGENERATED FILE -- 1680635796
#pragma once
#include <stddef.h>
diff --git a/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp b/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
index 6ae9863cb..c6f2b3895 100644
--- a/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
+++ b/src/bun.js/builtins/cpp/NodeEventsBuiltins.cpp
@@ -51,42 +51,43 @@ namespace WebCore {
const JSC::ConstructAbility s_nodeEventsOnAsyncIteratorCodeConstructAbility = JSC::ConstructAbility::CannotConstruct;
const JSC::ConstructorKind s_nodeEventsOnAsyncIteratorCodeConstructorKind = JSC::ConstructorKind::None;
const JSC::ImplementationVisibility s_nodeEventsOnAsyncIteratorCodeImplementationVisibility = JSC::ImplementationVisibility::Public;
-const int s_nodeEventsOnAsyncIteratorCodeLength = 4024;
+const int s_nodeEventsOnAsyncIteratorCodeLength = 4473;
static const JSC::Intrinsic s_nodeEventsOnAsyncIteratorCodeIntrinsic = JSC::NoIntrinsic;
const char* const s_nodeEventsOnAsyncIteratorCode =
"(function (emitter, event, options) {\n" \
" \"use strict\";\n" \
"\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
+ " var { AbortSignal, Number, Error } = globalThis;\n" \
"\n" \
- " //\n" \
- " var { Object, Number, console, Symbol } = globalThis;\n" \
- " //\n" \
+ " var AbortError = class AbortError extends Error {\n" \
+ " constructor(message = \"The operation was aborted\", options = void 0) {\n" \
+ " if (options !== void 0 && typeof options !== \"object\") {\n" \
+ " throw new Error(`Invalid AbortError options:\\n" \
+ "\\n" \
+ "${JSON.stringify(options, null, 2)}`);\n" \
+ " }\n" \
+ " super(message, options);\n" \
+ " this.code = \"ABORT_ERR\";\n" \
+ " this.name = \"AbortError\";\n" \
+ " }\n" \
+ " };\n" \
"\n" \
- " function isUndefinedOrNull(value) {\n" \
- " return value === undefined || value === null;\n" \
- " }\n" \
+ " if (@isUndefinedOrNull(emitter)) @throwTypeError(\"emitter is required\");\n" \
+ " //\n" \
+ " if (!(typeof emitter === \"object\" && @isCallable(emitter.emit) && @isCallable(emitter.on)))\n" \
+ " @throwTypeError(\"emitter must be an EventEmitter\");\n" \
"\n" \
- " if (isUndefinedOrNull(options)) options = {};\n" \
+ " if (@isUndefinedOrNull(options)) options = {};\n" \
"\n" \
" //\n" \
" var signal = options.signal;\n" \
- " //\n" \
- " //\n" \
+ " if (!@isUndefinedOrNull(signal) && !(signal instanceof AbortSignal))\n" \
+ " @throwTypeError(\"options.signal must be an AbortSignal\");\n" \
"\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
+ " if (signal?.aborted) {\n" \
+ " //\n" \
+ " throw new AbortError(@undefined, { cause: signal?.reason });\n" \
+ " }\n" \
"\n" \
" var highWatermark = options.highWatermark ?? Number.MAX_SAFE_INTEGER;\n" \
" if (highWatermark < 1) \n" \
@@ -105,9 +106,9 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" var size = 0;\n" \
" var listeners = [];\n" \
"\n" \
- " //\n" \
- " //\n" \
- " //\n" \
+ " function abortListener() {\n" \
+ " errorHandler(new AbortError(@undefined, { cause: signal?.reason }));\n" \
+ " }\n" \
"\n" \
" function eventHandler(value) {\n" \
" if (unconsumedPromises.isEmpty()) {\n" \
@@ -117,22 +118,22 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" emitter.pause();\n" \
" }\n" \
" unconsumedEvents.push(value);\n" \
- " } else unconsumedPromises.shift().@resolve.@call(undefined, [value]);\n" \
+ " } else unconsumedPromises.shift().@resolve.@call(@undefined, [value]);\n" \
" }\n" \
"\n" \
" function closeHandler() {\n" \
" removeAllListeners(listeners);\n" \
" finished = true;\n" \
" while (!unconsumedPromises.isEmpty()) {\n" \
- " unconsumedPromises.shift().@resolve.@call(undefined, [undefined]);\n" \
+ " const promise = unconsumedPromises.shift();\n" \
+ " promise.@resolve.@call(@undefined, [@undefined]);\n" \
" }\n" \
- " \n" \
- " return @createFulfilledPromise([undefined]);\n" \
+ " return @createFulfilledPromise([@undefined]);\n" \
" }\n" \
"\n" \
" function errorHandler(err) {\n" \
" if (unconsumedPromises.isEmpty()) error = err;\n" \
- " else unconsumedPromises.shift().@reject.@call(undefined, err);\n" \
+ " else unconsumedPromises.shift().@reject.@call(@undefined, err);\n" \
" \n" \
" closeHandler();\n" \
" }\n" \
@@ -140,37 +141,30 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" function addEventListener(emitter, event, handler) {\n" \
" emitter.on(event, handler);\n" \
" listeners.push([emitter, event, handler]);\n" \
- " //\n" \
" }\n" \
" \n" \
" function removeAllListeners() {\n" \
- " var heldEmitter;\n" \
" while (listeners.length > 0) {\n" \
- " //\n" \
" var entry = listeners.pop();\n" \
" var [emitter, event, handler] = entry;\n" \
- " if (event === \"foo\") heldEmitter = emitter;\n" \
- " console.log(emitter, event, handler);\n" \
" emitter.off(event, handler);\n" \
" }\n" \
- " console.log(heldEmitter.listenerCount(\"foo\"));\n" \
" }\n" \
"\n" \
- " var iterator = async function* NodeEventsOnAsyncIterator() {\n" \
- " //\n" \
- " while (!finished) {\n" \
- " if (size) {\n" \
- " var values = [];\n" \
- " while (size) {\n" \
- " values.push(unconsumedEvents.shift());\n" \
- " size--;\n" \
- " if (paused && size < lowWatermark) {\n" \
- " emitter.resume();\n" \
- " paused = false;\n" \
- " break;\n" \
- " }\n" \
+ " var createIterator = async function* NodeEventsOnAsyncIterator() {\n" \
+ " //\n" \
+ " try {\n" \
+ " while (true) {\n" \
+ " //\n" \
+ " while (size) {\n" \
+ " const value = unconsumedEvents.shift();\n" \
+ " size--;\n" \
+ " if (paused && size < lowWatermark) {\n" \
+ " emitter.resume();\n" \
+ " paused = false;\n" \
+ " break;\n" \
" }\n" \
- " yield @createFulfilledPromise(values);\n" \
+ " yield @createFulfilledPromise([value]);\n" \
" }\n" \
"\n" \
" //\n" \
@@ -181,67 +175,19 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" }\n" \
"\n" \
" //\n" \
- " if (finished) {\n" \
- " console.log(\"FINISHED\")\n" \
- " yield closeHandler();\n" \
- " break;\n" \
- " };\n" \
+ " if (finished) break;\n" \
"\n" \
" //\n" \
" var nextEventPromiseCapability = @newPromiseCapability(@Promise);\n" \
" unconsumedPromises.push(nextEventPromiseCapability);\n" \
" yield nextEventPromiseCapability.@promise;\n" \
" }\n" \
+ " } finally {\n" \
+ " closeHandler();\n" \
+ " }\n" \
" };\n" \
"\n" \
" //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- " //\n" \
- "\n" \
- " //\n" \
" addEventListener(emitter, event, eventHandler);\n" \
" if (event !== \"error\" && typeof emitter.on === \"function\") {\n" \
" addEventListener(emitter, \"error\", errorHandler);\n" \
@@ -253,23 +199,30 @@ const char* const s_nodeEventsOnAsyncIteratorCode =
" }\n" \
" }\n" \
"\n" \
- " //\n" \
- " //\n" \
+ " if (signal)\n" \
+ " signal.once(\"abort\", abortListener);\n" \
"\n" \
- " return {\n" \
- " throw: (err) => {\n" \
- " if (err === undefined || err === null || !(err instanceof Error)) {\n" \
- " @throwTypeError(\"The argument must be an instance of Error\");\n" \
- " }\n" \
- " errorHandler(err);\n" \
+ " var iterator = createIterator();\n" \
+ "\n" \
+ " @Object.defineProperties(iterator, {\n" \
+ " return: {\n" \
+ " value: function() {\n" \
+ " return closeHandler();\n" \
+ " },\n" \
" },\n" \
- " return: () => {\n" \
- " console.log(\"we're here\");\n" \
- " return closeHandler();\n" \
+ " throw: {\n" \
+ " value: function(err) {\n" \
+ " if (!err || !(err instanceof Error)) {\n" \
+ " throw new TypeError(\"EventEmitter.AsyncIterator must be called with an error\");\n" \
+ " }\n" \
+ " errorHandler(err);\n" \
+ " },\n" \
" },\n" \
- " next: () => iterator.next(),\n" \
- " [Symbol.asyncIterator]: iterator,\n" \
- " };\n" \
+ " [Symbol.asyncIterator]: {\n" \
+ " value: function() { return this; }\n" \
+ " },\n" \
+ " });\n" \
+ " return iterator;\n" \
"})\n" \
;
diff --git a/src/bun.js/builtins/js/NodeEvents.js b/src/bun.js/builtins/js/NodeEvents.js
index 02e944fde..6c80f81c8 100644
--- a/src/bun.js/builtins/js/NodeEvents.js
+++ b/src/bun.js/builtins/js/NodeEvents.js
@@ -26,36 +26,35 @@
function onAsyncIterator(emitter, event, options) {
"use strict";
- // var AbortError = class AbortError extends Error {
- // constructor(message = "The operation was aborted", options = void 0) {
- // if (options !== void 0 && typeof options !== "object") {
- // throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
- // }
- // super(message, options);
- // this.code = "ABORT_ERR";
- // this.name = "AbortError";
- // }
- // };
-
- // var { AbortSignal, Object, Number, console } = globalThis;
- var { Object, Number, console, Symbol } = globalThis;
- // console.log("AbortSignal", AbortSignal);
-
- function isUndefinedOrNull(value) {
- return value === undefined || value === null;
- }
+ var { AbortSignal, Number, Error } = globalThis;
+
+ var AbortError = class AbortError extends Error {
+ constructor(message = "The operation was aborted", options = void 0) {
+ if (options !== void 0 && typeof options !== "object") {
+ throw new Error(`Invalid AbortError options:\n\n${JSON.stringify(options, null, 2)}`);
+ }
+ super(message, options);
+ this.code = "ABORT_ERR";
+ this.name = "AbortError";
+ }
+ };
+
+ if (@isUndefinedOrNull(emitter)) @throwTypeError("emitter is required");
+ // TODO: Do a more accurate check
+ if (!(typeof emitter === "object" && @isCallable(emitter.emit) && @isCallable(emitter.on)))
+ @throwTypeError("emitter must be an EventEmitter");
- if (isUndefinedOrNull(options)) options = {};
+ if (@isUndefinedOrNull(options)) options = {};
// Parameters validation
var signal = options.signal;
- // if (!isUndefinedOrNull(signal) && !(signal instanceof AbortSignal))
- // @throwTypeError("options.signal must be an AbortSignal");
+ if (!@isUndefinedOrNull(signal) && !(signal instanceof AbortSignal))
+ @throwTypeError("options.signal must be an AbortSignal");
- // if (signal?.aborted) {
- // // TODO: Make this a builtin
- // throw new AbortError(undefined, { cause: signal?.reason });
- // }
+ if (signal?.aborted) {
+ // TODO: Make this a builtin
+ throw new AbortError(@undefined, { cause: signal?.reason });
+ }
var highWatermark = options.highWatermark ?? Number.MAX_SAFE_INTEGER;
if (highWatermark < 1)
@@ -74,9 +73,9 @@ function onAsyncIterator(emitter, event, options) {
var size = 0;
var listeners = [];
- // function abortListener() {
- // errorHandler(new AbortError(undefined, { cause: signal?.reason }));
- // }
+ function abortListener() {
+ errorHandler(new AbortError(@undefined, { cause: signal?.reason }));
+ }
function eventHandler(value) {
if (unconsumedPromises.isEmpty()) {
@@ -86,22 +85,22 @@ function onAsyncIterator(emitter, event, options) {
emitter.pause();
}
unconsumedEvents.push(value);
- } else unconsumedPromises.shift().@resolve.@call(undefined, [value]);
+ } else unconsumedPromises.shift().@resolve.@call(@undefined, [value]);
}
function closeHandler() {
removeAllListeners(listeners);
finished = true;
while (!unconsumedPromises.isEmpty()) {
- unconsumedPromises.shift().@resolve.@call(undefined, [undefined]);
+ const promise = unconsumedPromises.shift();
+ promise.@resolve.@call(@undefined, [@undefined]);
}
-
- return @createFulfilledPromise([undefined]);
+ return @createFulfilledPromise([@undefined]);
}
function errorHandler(err) {
if (unconsumedPromises.isEmpty()) error = err;
- else unconsumedPromises.shift().@reject.@call(undefined, err);
+ else unconsumedPromises.shift().@reject.@call(@undefined, err);
closeHandler();
}
@@ -109,40 +108,33 @@ function onAsyncIterator(emitter, event, options) {
function addEventListener(emitter, event, handler) {
emitter.on(event, handler);
listeners.push([emitter, event, handler]);
- // @arrayPush(listeners, [emitter, event, handler]);
}
function removeAllListeners() {
- var heldEmitter;
while (listeners.length > 0) {
- // var [emitter, event, handler] = @arrayPop(listeners);
var entry = listeners.pop();
var [emitter, event, handler] = entry;
- if (event === "foo") heldEmitter = emitter;
- console.log(emitter, event, handler);
emitter.off(event, handler);
}
- console.log(heldEmitter.listenerCount("foo"));
}
- var iterator = async function* NodeEventsOnAsyncIterator() {
- // First, we consume all unread events
- while (!finished) {
- if (size) {
- var values = [];
- while (size) {
- values.push(unconsumedEvents.shift());
- size--;
- if (paused && size < lowWatermark) {
- emitter.resume();
- paused = false;
- break;
- }
+ var createIterator = async function* NodeEventsOnAsyncIterator() {
+ // First, we consume all unread events
+ try {
+ while (true) {
+ // Go through queued events
+ while (size) {
+ const value = unconsumedEvents.shift();
+ size--;
+ if (paused && size < lowWatermark) {
+ emitter.resume();
+ paused = false;
+ break;
}
- yield @createFulfilledPromise(values);
+ yield @createFulfilledPromise([value]);
}
- // Then we error, if an error happened
+ // Check if error happened before yielding anything
// This happens one time if at all, because after 'error'
// we stop listening
if (error) {
@@ -150,66 +142,18 @@ function onAsyncIterator(emitter, event, options) {
}
// If the iterator is finished, break
- if (finished) {
- console.log("FINISHED")
- yield closeHandler();
- break;
- };
+ if (finished) break;
// Wait until an event happens
var nextEventPromiseCapability = @newPromiseCapability(@Promise);
unconsumedPromises.push(nextEventPromiseCapability);
yield nextEventPromiseCapability.@promise;
}
+ } finally {
+ closeHandler();
+ }
};
- // // TODO: Use builtin
- // Object.defineProperties(iterator, {
- // "throw": {
- // value: (err) => {
- // // TODO: Use Error builtin?
- // if (err === undefined || err === null || !(err instanceof Error)) {
- // @throwTypeError("The argument must be an instance of Error");
- // }
- // errorHandler(err);
- // },
- // },
- // "return": {
- // value: () => {
- // return closeHandler();
- // }
- // },
- // [Symbol.asyncIterator]: {
- // value: () => iterator,
- // },
- // // [kWatermarkData]: {
- // // /**
- // // * The current queue size
- // // */
- // // get size() {
- // // return size;
- // // },
- // // /**
- // // * The low watermark. The emitter is resumed every time size is lower than it
- // // */
- // // get low() {
- // // return lowWatermark;
- // // },
- // // /**
- // // * The high watermark. The emitter is paused every time size is higher than it
- // // */
- // // get high() {
- // // return highWatermark;
- // // },
- // // /**
- // // * It checks whether the emitter is paused by the watermark controller or not
- // // */
- // // get isPaused() {
- // // return paused;
- // // },
- // // },
- // });
-
// Adding event handlers
addEventListener(emitter, event, eventHandler);
if (event !== "error" && typeof emitter.on === "function") {
@@ -222,21 +166,28 @@ function onAsyncIterator(emitter, event, options) {
}
}
- // if (signal)
- // signal.once("abort", abortListener);
+ if (signal)
+ signal.once("abort", abortListener);
- return {
- throw: (err) => {
- if (err === undefined || err === null || !(err instanceof Error)) {
- @throwTypeError("The argument must be an instance of Error");
- }
- errorHandler(err);
+ var iterator = createIterator();
+
+ @Object.defineProperties(iterator, {
+ return: {
+ value: function() {
+ return closeHandler();
+ },
+ },
+ throw: {
+ value: function(err) {
+ if (!err || !(err instanceof Error)) {
+ throw new TypeError("EventEmitter.AsyncIterator must be called with an error");
+ }
+ errorHandler(err);
+ },
},
- return: () => {
- console.log("we're here");
- return closeHandler();
+ [Symbol.asyncIterator]: {
+ value: function() { return this; }
},
- next: () => iterator.next(),
- [Symbol.asyncIterator]: iterator,
- };
+ });
+ return iterator;
}
diff --git a/test/js/node/events/node-events.node.test.ts b/test/js/node/events/node-events.node.test.ts
index ac0e163ba..579d7ec5b 100644
--- a/test/js/node/events/node-events.node.test.ts
+++ b/test/js/node/events/node-events.node.test.ts
@@ -5,26 +5,19 @@ const { expect, assert, describe, it, createCallCheckCtx, createDoneDotAll } = c
// const NodeEventTarget = globalThis.EventTarget;
describe("node:events.on (EE async iterator)", () => {
- it("should return an async iterator", async done => {
- let resolveDeferred: () => void;
- const deferred = new Promise(resolve => {
- resolveDeferred = resolve as () => void;
- });
+ it("should return an async iterator", async () => {
const ee = new EventEmitter();
- process.nextTick(() => {
- ee.emit("foo", "bar");
- // 'bar' is a spurious event, we are testing
- // that it does not show up in the iterable
- ee.emit("bar", 24);
- ee.emit("foo", 42);
- resolveDeferred(); // Resolve in the next tick
- });
-
const iterable = on(ee, "foo");
+
+ ee.emit("foo", "bar");
+ // 'bar' is a spurious event, we are testing
+ // that it does not show up in the iterable
+ ee.emit("bar", 24);
+ ee.emit("foo", 42);
+
const expected = [["bar"], [42]];
for await (const event of iterable) {
- console.log(event);
const current = expected.shift();
assert.deepStrictEqual(current, event);
@@ -34,34 +27,29 @@ describe("node:events.on (EE async iterator)", () => {
}
}
- // This is necessary due to how we try to run emits on next tick
- deferred
- .then(() => {
- assert.strictEqual(ee.listenerCount("foo"), 0);
- assert.strictEqual(ee.listenerCount("error"), 0);
- })
- .catch(done);
+ assert.strictEqual(ee.listenerCount("foo"), 0);
+ assert.strictEqual(ee.listenerCount("error"), 0);
});
- // TODO: Fix undefined is not a function
- it.skip("should throw an error when the first argument is not an EventEmitter", async () => {
- expect(on({} as any, "foo")).toThrow(TypeError);
+ it("should throw an error when the first argument is not an EventEmitter", () => {
+ expect(() => on({} as any, "foo")).toThrow();
});
it("should throw an error when an error event is emitted", async () => {
const ee = new EventEmitter();
const _err = new Error("kaboom");
- process.nextTick(() => {
- ee.emit("error", _err);
- });
const iterable = on(ee, "foo");
+
+ ee.emit("error", _err);
+
let looped = false;
let thrown = false;
try {
// eslint-disable-next-line no-unused-vars
for await (const event of iterable) {
+ console.log("LOOPED?!");
looped = true;
}
} catch (err) {
@@ -72,133 +60,148 @@ describe("node:events.on (EE async iterator)", () => {
assert.strictEqual(looped, false);
});
- it("should throw when error emitted", async done => {
- let resolveDeferred: () => void;
- const deferred = new Promise(resolve => {
- resolveDeferred = resolve as () => void;
- });
-
+ it("should throw when error emitted after successful events", async () => {
const ee = new EventEmitter();
const _err = new Error("kaboom");
- process.nextTick(() => {
- ee.emit("foo", 42);
- ee.emit("error", _err);
- resolveDeferred();
- });
-
const iterable = on(ee, "foo");
- const expected = [[42]];
- const current = [] as (number[] | undefined)[];
- const received = [] as (number[] | undefined)[];
+ ee.emit("foo", 42);
+ ee.emit("error", _err);
+
+ const expected = [[42]] as (number[] | undefined[])[];
+
+ const current = [] as (number[] | undefined[])[];
+ const received = [] as (number[] | undefined[])[];
let thrownErr: any;
try {
for await (const event of iterable) {
- current.push(expected.shift());
+ const _expected = expected.shift();
+ if (_expected !== undefined) current.push(_expected);
received.push(event);
}
} catch (err) {
- console.log(err);
thrownErr = err;
}
- deferred
- .then(() => {
- assert.deepStrictEqual(current, received);
- assert.strictEqual(ee.listenerCount("foo"), 0);
- assert.strictEqual(ee.listenerCount("error"), 0);
+ assert.deepStrictEqual(current, received);
+ assert.strictEqual(ee.listenerCount("foo"), 0);
+ assert.strictEqual(ee.listenerCount("error"), 0);
- expect(thrownErr).toBeInstanceOf(Error);
- assert.strictEqual(thrownErr, _err);
- })
- .catch(done);
+ expect(thrownErr).toBeInstanceOf(Error);
+ assert.strictEqual(thrownErr, _err);
});
- // async function throwInLoop() {
- // const ee = new EventEmitter();
- // const _err = new Error("kaboom");
+ it("should throw when error thrown from inside loop", async () => {
+ const ee = new EventEmitter();
+ const _err = new Error("kaboom");
- // process.nextTick(() => {
- // ee.emit("foo", 42);
- // });
+ const iterable = on(ee, "foo");
- // try {
- // for await (const event of on(ee, "foo")) {
- // assert.deepStrictEqual(event, [42]);
- // throw _err;
- // }
- // } catch (err) {
- // assert.strictEqual(err, _err);
- // }
+ ee.emit("foo", 42);
- // assert.strictEqual(ee.listenerCount("foo"), 0);
- // assert.strictEqual(ee.listenerCount("error"), 0);
- // }
+ let looped = false;
+ let thrown = false;
- // async function next() {
- // const ee = new EventEmitter();
- // const iterable = on(ee, "foo");
+ try {
+ // eslint-disable-next-line no-unused-vars
+ for await (const event of iterable) {
+ assert.deepStrictEqual(event, [42]);
+ looped = true;
+ throw _err;
+ }
+ } catch (err) {
+ thrown = true;
+ assert.strictEqual(err, _err);
+ }
- // process.nextTick(function () {
- // ee.emit("foo", "bar");
- // ee.emit("foo", 42);
- // iterable.return();
- // });
+ assert.strictEqual(thrown, true);
+ assert.strictEqual(looped, true);
+ assert.strictEqual(ee.listenerCount("foo"), 0);
+ assert.strictEqual(ee.listenerCount("error"), 0);
+ });
- // const results = await Promise.all([iterable.next(), iterable.next(), iterable.next()]);
+ it("should allow for async iteration via .next()", async done => {
+ const ee = new EventEmitter();
+ const iterable = on(ee, "foo");
- // assert.deepStrictEqual(results, [
- // {
- // value: ["bar"],
- // done: false,
- // },
- // {
- // value: [42],
- // done: false,
- // },
- // {
- // value: undefined,
- // done: true,
- // },
- // ]);
+ process.nextTick(() => {
+ ee.emit("foo", "bar");
+ ee.emit("foo", 42);
+ // @ts-ignore
+ iterable.return();
+ });
- // assert.deepStrictEqual(await iterable.next(), {
- // value: undefined,
- // done: true,
- // });
- // }
+ const results = await Promise.all([iterable.next(), iterable.next(), iterable.next()]);
+ assert.deepStrictEqual(results, [
+ {
+ value: ["bar"],
+ done: false,
+ },
+ {
+ value: [42],
+ done: false,
+ },
+ {
+ value: undefined,
+ done: true,
+ },
+ ]);
+
+ assert.deepStrictEqual(await iterable.next(), {
+ value: undefined,
+ done: true,
+ });
- // async function nextError() {
- // const ee = new EventEmitter();
- // const iterable = on(ee, "foo");
- // const _err = new Error("kaboom");
- // process.nextTick(function () {
- // ee.emit("error", _err);
- // });
- // const results = await Promise.allSettled([iterable.next(), iterable.next(), iterable.next()]);
- // assert.deepStrictEqual(results, [
- // {
- // status: "rejected",
- // reason: _err,
- // },
- // {
- // status: "fulfilled",
- // value: {
- // value: undefined,
- // done: true,
- // },
- // },
- // {
- // status: "fulfilled",
- // value: {
- // value: undefined,
- // done: true,
- // },
- // },
- // ]);
- // assert.strictEqual(ee.listeners("error").length, 0);
- // }
+ done();
+ });
+
+ it("it should fulfill subsequent deferred promises with `undefined` when the emitter emits an error", async done => {
+ const ee = new EventEmitter();
+ const iterable = on(ee, "foo");
+ const _err = new Error("kaboom");
+
+ process.nextTick(function () {
+ ee.emit("error", _err);
+ });
+
+ const results = await Promise.allSettled([iterable.next(), iterable.next(), iterable.next()]);
+
+ assert.deepStrictEqual(results, [
+ {
+ status: "rejected",
+ reason: _err,
+ },
+ {
+ status: "fulfilled",
+ value: {
+ value: undefined,
+ done: true,
+ },
+ },
+ {
+ status: "fulfilled",
+ value: {
+ value: undefined,
+ done: true,
+ },
+ },
+ ]);
+
+ assert.strictEqual(ee.listeners("error").length, 0);
+
+ done();
+ });
+
+ it("should throw a `TypeError` when calling throw without args", async () => {
+ const ee = new EventEmitter();
+ const iterable = on(ee, "foo");
+
+ expect(() => {
+ // @ts-ignore
+ iterable.throw();
+ }).toThrow(TypeError);
+ });
// async function iterableThrow() {
// const ee = new EventEmitter();