New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@byojs/eventer

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@byojs/eventer - npm Package Compare versions

Comparing version 0.0.2 to 0.1.0

2

package.json
{
"name": "@byojs/eventer",
"description": "Event emitter with async-emit and weak-listener support",
"version": "0.0.2",
"version": "0.1.0",
"exports": {

@@ -6,0 +6,0 @@ "./": "./dist/eventer.mjs"

@@ -238,4 +238,30 @@ # Eventer

myMap.on("position-update",onPositionUpdate);
// elsewhere:
myMap.emit("position-update",centerX,centerY);
```
### `AbortSignal` unsubscription
A recent welcomed change to the [native `addEventListener(..)` browser API](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) is the ability to pass in an [`AbortSignal` instance](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal), from an [`AbortController` instance](https://developer.mozilla.org/en-US/docs/Web/API/AbortController); if the `"abort"` event is fired on the signal, [the event listener is unsubscribed](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#signal), instead of having to manually call [`removeEventListener(..)`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener) to unsubscribe. This is helpful because you don't need keep around any reference to the listener function to unsubscribe it.
**Eventer** also supports this functionality:
```js
function onWhatever() {
console.log("'whatever' event fired!");
}
var ac = new AbortController();
// subscribe to "whatever" event, but set up
// the abort-signal to unsubscribe
events.on("whatever",onWhatever,{ signal: ac.signal });
// later:
ac.abort("Unsubscribe!");
```
**Note:** An `AbortSignal` instance is also held weakly by **Eventer**, so any GC of either the listener or the signal will drop the relationship between them as desired -- without one preventing GC of the other.
### Inline event listeners (functions)

@@ -356,3 +382,3 @@

After the call to `listenToWhatever()`, any `"whatever"` events fire may be handled or not, unpredictably, because the inner `=>` arrow function is now subject to GC cleanup at any point the JS engine feels like it!
After the call to `listenToWhatever()`, any `"whatever"` events fired, may be handled or not, unpredictably, because the inner `=>` arrow function is now subject to GC cleanup at any point the JS engine feels like it!

@@ -359,0 +385,0 @@ ### `once(..)` Method

@@ -7,3 +7,19 @@ // Parts of this implementation adapted from:

var finalization = new FinalizationRegistry(
({ refs, ref, }) => removeFromList(refs,ref)
({ refs, ref, signalRefs, }) => {
removeFromList(refs,ref);
if (signalRefs != null) {
for (
let { signalRef, onAbortSignalRef, } of
Object.values(signalRefs)
) {
// note: these may very well have already been
// GC'd, so there may be nothing to do here
let signal = signalRef.deref();
let onAbortSignal = onAbortSignalRef.deref();
if (signal != null && onAbortSignal != null) {
signal.removeEventListener("abort",onAbortSignal);
}
}
}
}
);

@@ -52,3 +68,6 @@ var Eventer = defineEventerClass();

on(eventName,listener) {
on(eventName,listener,{ signal, } = {}) {
// already-aborted AbortSignal passed in?
if (signal != null && signal.aborted) return false;
// if not in "weak-listeners" mode, store a

@@ -83,2 +102,39 @@ // reference to prevent GC

// AbortSignal passed in?
if (signal != null) {
// weakly hold reference to signal, to
// remove its event listener later
let signalRef = new WeakRef(signal);
// handler for when signal is aborted
let onAbortSignal = () => {
// weak reference still points at a
// signal?
var theSignal = signalRef.deref();
var theHandler = onAbortSignalRef.deref();
if (theSignal != null && theHandler != null) {
theSignal.removeEventListener("abort",theHandler);
}
// weak reference still points at a
// listener?
var listener = listenerRef.deref();
if (listener != null) {
this.off(eventName,listener);
}
};
let onAbortSignalRef = new WeakRef(onAbortSignal);
signal.addEventListener("abort",onAbortSignal);
// save signal/handler weak references for later
// unsubscription, upon GC of listener
listenerEntry.signalRefs = {
[eventName]: {
signalRef,
onAbortSignalRef,
},
};
}
// listen for GC of listener, to unregister any

@@ -91,2 +147,3 @@ // event subscriptions (clean up memory)

ref: listenerRef,
signalRefs: listenerEntry.signalRefs,
},

@@ -100,2 +157,4 @@ listenerRef

else if (!listenerEntry.events.includes(eventName)) {
let listenerRef = listenerEntry.ref;
// register event on listener entry

@@ -108,4 +167,40 @@ listenerEntry.events.push(eventName);

);
this.#listenerRefsByEvent[eventName].push(listenerEntry.ref);
this.#listenerRefsByEvent[eventName].push(listenerRef);
// AbortSignal passed in?
if (signal != null) {
// weakly hold reference to signal, to
// remove its event listener later
let signalRef = new WeakRef(signal);
// handler for when signal is aborted
let onAbortSignal = () => {
// weak reference still points at a
// signal?
var theSignal = signalRef.deref();
var theHandler = onAbortSignalRef.deref();
if (theSignal != null && theHandler != null) {
theSignal.removeEventListener("abort",theHandler);
}
// weak reference still points at a
// listener?
var listener = listenerRef.deref();
if (listener != null) {
this.off(eventName,listener);
}
};
let onAbortSignalRef = new WeakRef(onAbortSignal);
signal.addEventListener("abort",onAbortSignal);
// save signal/handler weak references for later
// unsubscription, upon GC of listener
listenerEntry.signalRefs = listenerEntry.signalRefs ?? {};
listenerEntry.signalRefs[eventName] = {
signalRef,
onAbortSignalRef,
};
}
return true;

@@ -117,4 +212,4 @@ }

once(eventName,listener) {
if (this.on(eventName,listener)) {
once(eventName,listener,opts) {
if (this.on(eventName,listener,opts)) {
// (weakly) remember that this is a "once"

@@ -192,2 +287,12 @@ // registration (to unregister after first

}
// abort signal (for event) to clean up?
if (listenerEntry.signalRefs?.[eventName] != null) {
let signal = listenerEntry.signalRefs[eventName].signalRef.deref();
let onAbortSignal = listenerEntry.signalRefs[eventName].onAbortSignalRef.deref();
if (signal != null && onAbortSignal != null) {
signal.removeEventListener("abort",onAbortSignal);
}
delete listenerEntry.signalRefs[eventName];
}
}

@@ -211,2 +316,17 @@ else {

}
// abort signal(s) to cleanup?
if (listenerEntry.signalRefs != null) {
for (
let { signalRef, onAbortSignalRef, } of
Object.values(listenerEntry.signalRefs)
) {
let signal = signalRef.deref();
let onAbortSignal = onAbortSignalRef.deref();
if (signal != null && onAbortSignal != null) {
signal.removeEventListener("abort",onAbortSignal);
}
}
delete listenerEntry.signalRefs;
}
}

@@ -213,0 +333,0 @@

@@ -26,2 +26,3 @@ // note: this module specifier comes from the import-map

var runWeakTestsPart2Btn = document.getElementById("run-weak-tests-part-2-btn");
var runWeakTestsPart3Btn = document.getElementById("run-weak-tests-part-3-btn");
testResultsEl = document.getElementById("test-results");

@@ -32,2 +33,3 @@

runWeakTestsPart2Btn.addEventListener("click",runWeakTestsPart2);
runWeakTestsPart3Btn.addEventListener("click",runWeakTestsPart3);

@@ -114,2 +116,11 @@ try {

false,
true,
"A: 20 (true)",
true,
false,
false,
false,
true,
false,
false
];

@@ -138,2 +149,6 @@

var onFnBound = events.on.bind(events);
var AC1 = new AbortController();
var AC2 = new AbortController();
var AS1 = AC1.signal;
var AS2 = AC2.signal;

@@ -188,2 +203,12 @@ results.push( onFnBound("test",A) );

results.push( events3.off("test",A3) );
results.push( events.on("test",A,{ signal: AS1, }) );
results.push( events.emit("test",counter++) );
AC1.abort("unsubscribe-1");
results.push( events.emit("test",counter++) );
results.push( events.off("test",A) );
results.push( events.on("test",A,{ signal: AS1, }) );
results.push( events.once("test",A,{ signal: AS2, }) );
AC2.abort("unsubscribe-2");
results.push( events.emit("test",counter++) );
results.push( events.off("test",A) );

@@ -396,3 +421,8 @@ if (JSON.stringify(results) == JSON.stringify(expected)) {

},
E(msg) {
weakTests.results.push(`E: ${msg}`);
},
};
var EController = new AbortController();
var ESignal = EController.signal;
weakTests.events1 = new Eventer({ asyncEmit: false, weakListeners: true, });

@@ -406,4 +436,6 @@ weakTests.events2 = new Eventer({ asyncEmit: false, weakListeners: false, });

weakTests.finalization.register(weakTests.listeners.B,"B");
weakTests.finalization.register(weakTests.listeners.B,"C");
weakTests.finalization.register(weakTests.listeners.C,"C");
weakTests.finalization.register(weakTests.listeners.D,"D");
weakTests.finalization.register(weakTests.events3,"events3");
weakTests.finalization.register(ESignal,"E.signal");

@@ -428,2 +460,4 @@ try {

weakTests.events1.on("test-4",weakTests.listeners.E,{ signal: ESignal, });
weakTests.results.push( weakTests.events1.emit("test",counter++) );

@@ -443,4 +477,6 @@ weakTests.results.push( weakTests.events2.emit("test",counter++) );

weakTests.listeners = null;
weakTests.EController = EController;
weakTests.ESignal = ESignal;
testResultsEl.innerHTML += "<br><strong>NOW: Please trigger a GC event in the browser</strong> before running the <em>part 2</em> tests.<br><small>(see instructions above for Chrome or Firefox browsers)<br><br>";
testResultsEl.innerHTML += "<br><strong>NEXT: Please trigger a GC event in the browser</strong> before running the <em>part 2</em> tests.<br><small>(see instructions above for Chrome or Firefox browsers)<br><br>";

@@ -470,2 +506,3 @@ document.getElementById("run-weak-tests-part-2-btn").disabled = false;

"removed: C",
"removed: D",
"removed: events3",

@@ -477,3 +514,2 @@ false,

];
weakTests.finalization = null;

@@ -497,2 +533,10 @@ try {

testResultsEl.innerHTML += "(Weak Tests Part 2) PASSED.<br>";
weakTests.results.length = 0;
// allow GC of abort-controller/signal (for part 3)
weakTests.EController = weakTests.ESignal = null;
testResultsEl.innerHTML += "<br><strong>LASTLY: Please trigger *ONE MORE* GC event in the browser</strong> before running the <em>part 3</em> tests.<br><small>(see instructions above for Chrome or Firefox browsers)<br><br>";
document.getElementById("run-weak-tests-part-3-btn").disabled = false;
return true;

@@ -511,2 +555,37 @@ }

document.getElementById("run-weak-tests-part-2-btn").disabled = true;
}
return false;
}
async function runWeakTestsPart3() {
testResultsEl.innerHTML += "Running weak tests (part 3)...<br>";
var expected = [
"removed: E.signal",
];
weakTests.finalization = null;
try {
// normalize unpredictable finalization-event ordering
weakTests.results.sort((v1,v2) => (
typeof v1 == "string" ? (
typeof v2 == "string" ? v1.localeCompare(v2) : 0
) : 1
));
if (JSON.stringify(weakTests.results) == JSON.stringify(expected)) {
testResultsEl.innerHTML += "(Weak Tests Part 3) PASSED.<br>";
return true;
}
else {
testResultsEl.innerHTML += "(Weak Tests Part 3) FAILED.<br><br>";
reportExpectedActual(expected,weakTests.results);
}
}
catch (err) {
logError(err);
testResultsEl.innerHTML = "(Weak Tests Part 3) FAILED -- see console.";
}
finally {
document.getElementById("run-weak-tests-part-2-btn").disabled = true;
document.getElementById("run-weak-tests-part-3-btn").disabled = true;
weakTests = {};

@@ -513,0 +592,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc