evnty
Version:
Async-first, reactive event handling library for complex event flows in browser and Node.js
135 lines (133 loc) • 3.38 kB
JavaScript
import { Disposer } from "./async.js";
import { Signal } from "./signal.js";
import { ListenerRegistry } from "./listener-registry.js";
import { DispatchResult } from "./dispatch-result.js";
export class EventIterator {
#signal;
constructor(signal){
this.#signal = signal;
}
async next() {
try {
const value = await this.#signal.receive();
return {
value,
done: false
};
} catch {
return {
value: undefined,
done: true
};
}
}
async return() {
return {
value: undefined,
done: true
};
}
}
export class Event {
#listeners = new ListenerRegistry();
#signal = new Signal();
#disposer;
#onDispose;
#sink;
[Symbol.toStringTag] = 'Event';
constructor(onDispose){
this.#onDispose = onDispose;
this.#disposer = new Disposer(this);
}
get sink() {
return this.#sink ??= this.emit.bind(this);
}
handleEvent(event) {
this.emit(event);
}
get size() {
return this.#listeners.size;
}
emit(value) {
this.#signal.emit(value);
return new DispatchResult(this.#listeners.dispatch(value));
}
lacks(listener) {
return !this.#listeners.has(listener);
}
has(listener) {
return this.#listeners.has(listener);
}
off(listener) {
this.#listeners.off(listener);
return this;
}
on(listener) {
this.#listeners.on(listener);
return ()=>void this.off(listener);
}
once(listener) {
this.#listeners.once(listener);
return ()=>void this.off(listener);
}
clear() {
this.#listeners.clear();
return this;
}
receive() {
return this.#signal.receive();
}
then(onfulfilled, onrejected) {
return this.receive().then(onfulfilled, onrejected);
}
catch(onrejected) {
return this.receive().catch(onrejected);
}
finally(onfinally) {
return this.receive().finally(onfinally);
}
settle() {
return this.receive().then((value)=>({
status: 'fulfilled',
value
}), (reason)=>({
status: 'rejected',
reason
}));
}
[Symbol.asyncIterator]() {
return new EventIterator(this.#signal);
}
dispose() {
this[Symbol.dispose]();
}
[Symbol.dispose]() {
if (this.#disposer[Symbol.dispose]()) {
this.#signal[Symbol.dispose]();
this.#listeners.clear();
void this.#onDispose?.();
}
}
}
export const merge = (...events)=>{
const mergedEvent = new Event(()=>{
for (const event of events){
event.off(mergedEvent.sink);
}
});
for (const event of events){
event.on(mergedEvent.sink);
}
return mergedEvent;
};
export const createInterval = (interval)=>{
let counter = 0;
const intervalEvent = new Event(()=>clearInterval(timerId));
const timerId = setInterval(()=>{
intervalEvent.emit(counter++);
}, interval);
return intervalEvent;
};
export const createEvent = ()=>new Event();
export default createEvent;
//# sourceMappingURL=event.js.map