Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One time event handling using promises?

Pretty much usual scenario. I want to have some decoupled piece of code, that is triggering event when something is ready. This will happen only once for the whole application run.

On other side, there is another piece of code, where I want something else to happen when two or more events are triggered. I mean like all of them, like dependencies.

Alright, more async things together ... definitely promises right ?

Then I started thinking. Is it really wise to use pub/sub for one time events ? Wouldn't be better to just make accessible promise, that resolves once that event is about to be triggered ? However that would mean I need to kinda interconnect that decoupled code. One thing is having shared EventEmitter, but being dependent on some code to actually create promise ... that sounds rather bad.

So I am thinking about some kind of mix. Having module, where other modules can ask for "event" by it's name and obtaining prepared Promise object. Other module should then trigger that event and effectively fulfilling/rejecting that event this way.

var promisedLand = require('./promisedLand');
promisedLand.waitFor('event'); // returns promise
promisedLand.resolve('event', value);
promisedLand.reject('event', error);

What do you think about this solution ? Any chance there is some solution like this already available ?

like image 296
FredyC Avatar asked Apr 16 '14 16:04

FredyC


People also ask

What are the advantages of using promises instead of callbacks?

They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events. In other words also, we may say that, promises are the ideal choice for handling multiple callbacks at the same time, thus avoiding the undesired callback hell situation.

How do you handle a promise response?

To support error handling, Promise objects provide a catch() method. This is a lot like then() : you call it and pass in a handler function. However, while the handler passed to then() is called when the asynchronous operation succeeds, the handler passed to catch() is called when the asynchronous operation fails.

How does promise work in event loop?

A promise represents the completion of an asynchronous function. It is an object that might return a value in the future. It accomplishes the same basic goal as a callback function, but with many additional features and a more readable syntax.

Are promises asynchronous?

A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed.


2 Answers

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.

like image 63
Benjamin Gruenbaum Avatar answered Sep 29 '22 19:09

Benjamin Gruenbaum


Alright, I have made my own solution similar to the one presented in the question.

Welcome to the Promised Land

like image 39
FredyC Avatar answered Sep 29 '22 20:09

FredyC