Good question. Let me start by one thing: Promises are not event emitters.
Let me reiterate that, since it's a misconception that comes a lot. Promises are not event emitters. With progression, they can be hacked into a form of crippled event emitter but at the end of the day. Promises are not event emitters.
Promises
What are they? Promises are a "box" over a value which you can open at some point using the .then
method and then put the result in another box. Nothing more, nothing less.
Like you said, promises are one time. If your event is a one time event - then promises are definitely ok. Essentially, your event is an eventuality and promises model it better most of the time.
Promises as emitters
The problem with using promises as event emitters is composition, progression events in promises simply did not compose very well. Promises chain and compose and events don't. This is why the Q library is dumping progression in favor of estimation in v2. This is why progression was never included in ECMAScript 6.
Event emitters are a perfectly fine abstraction on their own, use event emitters when they're the right tool to model your relation (pub-sub), use promises when they're the right tool to model your relation, use streams when they're the right tool to model your relation. Just don't use one tool for everything as this (from experience) will bring you nothing but a lot of pain.
What I described in my question is really cool though, what about that?
What you're looking for? Oh, that exist. It's quite wonderful actually though it also comes with its own sets of problems.
What you're looking for is called FRP - functional reactive programming. There are a lot of libraries that do that with the best one (in my opinion) being BaconJS.
FRP has the notion of observables you were talking about. Here is an example of a counter from the BaconJS website:
var up = $('#up').asEventStream('click');
var down = $('#down').asEventStream('click');
var counter =
// map up to 1, down to -1
up.map(1).merge(down.map(-1))
// accumulate sum
.scan(0, function(x,y) { return x + y });
// assign observable value to jQuery property text
counter.assign($('#counter'), 'text');
Very similar to promises in chaining, but does not represent a direct continuation but a continuous streaming and a sink.
FRP is a very common and developed paradigm in functional languages like Haskell, and is very applicable in JavaScript. It's not very common yet and it has its own shortcomings, but it definitely thinks like what you had in mind.
So, short recap:
- Promises are not event emitters. Promises are awesome and majestic and solve a lot of interesting concurrency problems - but they are not a silver bullet for all your decoupled flow control.
- FRP is this cool thing you came up with but didn't formulate yet. It has a notion of observables and events exactly as you described in your question.
Also, you can pat yourself on the back for thinking about a paradigm without knowing it on your own. You deserve it, honest.