-
-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EventEmitter realisation #279
base: ee
Are you sure you want to change the base?
Conversation
Example: const ee = new EventEmitter(); const fn1 = () => console.log('first'); const fn2 = () => console.log('second'); ee.once('data', fn1); ee.once('data', fn2); ee.off('data', fn1); ee.emit('data'); Output: first second As a solution I store seperatly functions added via once and on.
Example: const ee = new EventEmitter(); setTimeout(() => ee.emit('data'), 3000); ee.toPromise('data').then(() => console.log('Event emited'));
Example: (async () => { const ee = new EventEmitter(); let index = 0; setInterval(() => ee.emit('data', index++, index), 1000); for await (const args of ee.toIterator('data')) { console.log({ args }); } })();
The structure of 'wrappers' field used to look like this: Map { 'eventName' => Map { [Function: origin] => [Function: wrapper] }, 'otherEvent' => Map { [Function: otherOrigin] => [Function: otherWrapper] }, ... } But it made the work harder, especially in the 'listeners' method: listeners(eventName) { const { events, wrappers } = this; const listeners = events.get(eventName); const callbacks = wrappers.get(eventName); const reversed = reverseMap(callbacks); if (!listeners) return []; const result = []; for (const listener of listeners) { const isWrapped = reversed.has(listener); const output = isWrapped ? reversed.get(listener) : listener; result.push(output); } return result; } We needed the 'reverseMap' function to get all the listeners. Here you can see that we are iterating over all listeners from events, which means we just need to determine if the listener is wrapped. So I propose to make mixins to all wrapped functions with an 'origin' field that references the original listener. I really don't like mixins, but since we work with wrapped functions only inside the program abstraction and don't expose them outside, I think this is a normal approach that will simplify the code and make it more efficient. And now 'wrappers' field looks like: Map { [Function: origin] => [Function: wrapper], [Function: otherOrigin] => [FUnction: otherWrapper], ... }
(async () => { const ee = new EventEmitter(); const signal = AbortSignal.timeout(1000); let index = 0; const timer = setInterval(() => ee.emit('data', index++), 300); try { for await (const data of ee.toIterator('data', { signal })) { console.log({ data }); } } catch (error) { console.log(error.message); } clearInterval(timer); })();
this.off(name, dispose); | ||
return void fn(...args); | ||
once(eventName, listener) { | ||
const wrapper = (...args) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dispose
const { signal = null } = options; | ||
return new Promise((resolve, reject) => { | ||
if (!signal) { | ||
return void this.once(eventName, (...args) => void resolve(args)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need use cases because we can't imagine all developer experience without textual representation
let onAbort = null; | ||
const onSuccess = (...args) => { | ||
signal.removeEventListener('abort', onAbort); | ||
resolve(args); | ||
}; | ||
onAbort = () => { | ||
this.off(eventName, onSuccess); | ||
const message = 'The operatopn was aborted'; | ||
reject(new Error(message, { cause: signal.reason })); | ||
}; | ||
this.once(eventName, onSuccess); | ||
signal.addEventListener('abort', onAbort); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let onAbort = null; | |
const onSuccess = (...args) => { | |
signal.removeEventListener('abort', onAbort); | |
resolve(args); | |
}; | |
onAbort = () => { | |
this.off(eventName, onSuccess); | |
const message = 'The operatopn was aborted'; | |
reject(new Error(message, { cause: signal.reason })); | |
}; | |
this.once(eventName, onSuccess); | |
signal.addEventListener('abort', onAbort); | |
const handlers = { | |
success: (...args) => { | |
signal.removeEventListener('abort', handlers.abort); | |
resolve(args); | |
}, | |
abort: () => { | |
this.off(eventName, handlers.success); | |
const message = 'The operation was aborted'; | |
reject(new Error(message, { cause: signal.reason })); | |
}, | |
}; | |
this.once(eventName, handlers.success); | |
signal.addEventListener('abort', handlers.abort); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need research
Sorry if I messed up with the PR design, this is my first time. I will be glad to receive your criticism