This is actually much more simple than you're all making it. The
Node.js API is remarkably consistent on this point.
1. Callback
A callback is for cases where you have a single "question", which will
be answered by either an error, or optionally some data.
getSomeThing(arg, arg, arg, function (er, someThing) {
if (er) return ohnoes(er)
// .. now do something with the someThing
})
Callbacks should be called *exactly* once.
2. EventEmitter
EventEmitter : Callback :: Class : Function
An EventEmitter is used in cases where a single object may emit a
bunch of different messages, and you want to handle some of them. If
an EventEmitter encounters an error, it emits an 'error' event, which
throws if it is unhandled.
var ee = new EventEmitter
ee.on('someEvent', function (data) {
// some event happened, and wants to tell me about data
})
ee.on('error', function (er) {
ohnoes(er)
})
EventEmitters are highly optimized, and very cheap to create and use.
(They need to be, because node internally creates bazillions of them.)
You can easily have a class that extends EventEmitter to add
additional semantics:
function Foo() {}
util.inherits(Foo, EventEmitter)
Foo.protoype.getPrimes = function (n) {
if (this._gettingPrimes) {
this.emit('error', new Error('already getting some!'))
}
this._gettingPrimes = true
// go and get some primes.
// when you have one:
this.emit('prime', thePrime)
// maybe when you're done:
this._gettingPrimes = false
this.emit('end')
})
3. Streams
Streams : Time :: Array : Space
A Stream is a special EventEmitter subclass designed for dealing with
data flowing into or out of some underlying interface, such as a file
or socket. It's used throughout Node, though a bit clumsily in
places, and the interface and usage is scheduled to be cleaned up in
v0.9.
Readable streams have a .pipe() method that can be used to send the
bytes coming *out* of the readable stream *into* the writable stream.
For this prime generator thing, presumably each prime number takes
some relevant amount of time to generate, but it doesn't sound much
like a Stream, per se. I'd subclass EventEmitter, and just define
semantics for your events that makes sense for your specific use case.