-
Notifications
You must be signed in to change notification settings - Fork 7.3k
Event emitter calls listeners immediately, rather than on nextTick #8470
Comments
Edit: my finger slipped on submit -- apologies! Hold on a second, I have a bit more to write up :) Emitting an event on an emitter instance means, in part, to execute all of the emitter's registered listeners for that event, sequentially. This is one of the oldest patterns Node uses, predating streams, the modern
"emitting" means to "execute all listeners for this topic on this emitter with these arguments". Event emitters are not a flow control pattern -- they don't involve the event loop at all. It's sort of an application of the Observer pattern in JavaScript. |
Sorry again for the accidental early submission. Continuing... Re: setImmediate(function() {
eventEmitter.emit('topic', args)
}) I vehemently agree that there should be more documentation on EventEmitters practices -- they are a powerful footgun. At present, however, Node's documentation is heavily (nearly exclusively!) reference-focused. Reference docs are random-access. There's no guarantee that the reader is consuming the document linearly, and thus it's hard to ensure that necessary information is conveyed. I'm leery of interleaving best-practice documentation with the existing reference docs -- there's a good chance people who need them won't see them, and people who don't need to see them will get bogged down by them. I'd like to revisit this when we get a place for non-reference docs, which I believe some folk are working towards. |
Thanks for the quick response! Heh, I like the 'vehement' agreement about the documentation. I don't mind where it goes, so much as I am looking for more than I've found. I did go digging (maybe not well?), and came up with a really nice article on error handling best practices, but not on emitted events. Do you know of any? If people are working on this...fantastic! I've started to use |
A specific case is in the emitting of As for the differences between
Another reason is to ensure resources are properly cleaned up. This is actually an edge case issue in Node that still needs to be fixed. Here's an example: (function createServer() {
require('net').createServer()
.on('close', createServer)
.listen(8080, function() { this.close(); });
}()); Because the Hopefully that sheds some light. @chrisdickinson Feel free to throw this into some documentation somewhere. ;-) |
Ah, yes it does, thank you! |
@chrisdickinson think this can be closed now? |
Yep! Closing now. |
This seems to have been an intentional design decision, but if so, I've been unable to find any commentary. Also, if so, then this is really a request for a new API, or at least some documentation of best practices for event emitters.
My mental model for emitting an event is that "emitting" is independent of the listener "hearing" and acting. In concrete terms, when code looks like this:
I expect the outcome to be:
Instead, it's
Rather than being "emit my event", the present behavior is actually "execute arbitrary code". That seems like broken behavior, and I know from conversations with other developers and inspection of library code that I'm not alone in my expectation of "emit" being independent from listener execution.
The only place I've seen the current behavior become a problem is when you are expecting state to be safe on the other side of an 'emit' call:
This is a trivial example, but I've actually run into a more complex situation crossing multiple libraries and emitted events which eventually boiled down to this. The library I was using got a socket error, seemingly in the middle of executing a synchronous function, which I didn't think was possible. In the
socket.on('error', ...)
callback, the library cleaned up its state before returning to the prior execution flow (which depended on that state). I was totally confused until I debugged the actual execution flow and realized that it was all due to events emitting events emitting events, and that all of that happens on this tick.I'll be submitting a bug/PR to the library's maintainers, but this behavior was so counter-intuitive to me that I had to submit a bug with node, as well.
So, is this purposeful behavior? If not, could it be changed, or a new "emitNext" be added? Either way, could some documentation be added explaining the risks and providing some best-practices?
The text was updated successfully, but these errors were encountered: