Writing code in an async programming framework like node.js becomes super complex and unbearably ugly super fast. You are dealing with synchronous and asynchronous functions and mixing them together can get really tricky. That is why we have Control Flow.
In computer science, control flow (or alternatively, flow of control) refers to the order in which the individual statements, instructions, or function calls of an imperative or a declarative program are executed or evaluated.
Basically, control flow libraries allow you to write non-blocking code that executes in an asynchronous fashion without writing too much glue or plumbing code. There is a whole section on control-flow modules to help you unwind the sphagetti of callbacks that you will quickly land yourself into while dealing with async calls in node.js.
Control flow as a pattern can been implemented via several approaches and one of such approaches is called Promises.
The reason why people are talking about Promises and other similar patterns now is partly because of two reasons in my opinion:
- The second reason in my opinion is node.js. Thanks to node.js, people are now writing all the backend code in async fashion, backend code where all the heavylifting is done to generate the right response from a multitude of data sources. All these data sources are queried in async fashion in node.js and there is an urgent need of control flow to manage concurrency.
Because of the above two reasons, a lot of frameworks have started providing the concept of Promises out of the box. CommonJS has realized that the pattern needs to be standardized across these various frameworks and has a spec now, http://wiki.commonjs.org/wiki/Promises
JQuery 1.5 introduced the Deferred object, which is another name for the Promises pattern. It is based of the PromisesA CommonJS spec.
Ok, so now that its clear that Promises is a pattern, what exactly is it and how is it different from other traditional approaches?
In computer science, future, promise, and delay refer to constructs used for synchronization in some concurrent programming languages. They describe an object that acts as a proxy for a result that is initially not known, usually because the computation of its value has not yet completed.
Yep, that’s nice in theory but in practice, this is what changes:
Looks almost the same thing. Infact, it looks like Promises can hide the true nature of a function. With Promises, it can be super hard to tell if a call is synchronous or asynchronous. I say so because this is how your JsDoc for your async call function would change.
Some people will argue second style is cleaner. I am more in favour with the first style on this one.
If you are a small scrappy startup with fast growing codebase, It’s really hard to have proper documentation. In such scenarios where proper documentation is missing, Promises are a deal breaker for me as more than once, you will have to look within a function just to understand how to consume it and whether its asynchronous or not. With the first style, the function definition is very explicit and usage doesn’t rely heavily on the documentation. If there is a callback, it most likely is asynchronous.
Having said that, there are some pros in the Promises approach.
The first one that is usually talked about is how Promises are good for dealing with a multitude of async/sync calls. Taming multiple async calls becomes much easier with Promises.
Imagine a typical scenario where one has to perform something only after a list of tasks are done. These tasks can be both asynchronous or synchronous in nature in themselves. With Promises, one can write code that functions identically calling these synchronous and asynchronous functions, without getting into the nested callback hell that might otherwise happen.
This function returns a promise, based on the results of a bunch of tasks. These tasks are a mix of async and sync calls. Notice how in the code, It does not have to distinguish between those calls. This is a huge benefit to most of the Promises implementations.
Typically, all Control-flow libraries following other patterns also provide this benefit though. An example is async module in node.js modules. After all, this is the entire reason behind control-flow. The difference between Promises based implementations vs callback based implementations is suttle and more a matter of choice than anything else.
Another advantage that is given to Promises based implementations is how they are helpful in attaching multiple callbacks for an async operation. With Promises, you can attach listeners to an async operation via done even after the async call has completed. If attached after completion, that callback is immediately invoked with the response.
Traditionally, we have been solving this problem using Events. An event is dispatched when an async task completes and all those who care, can subscribe to the event prior and execute on it when the event happens. The typical problem with Events though is that you have to subscribe early else you can miss an event.
With Promises, You can attach those callbacks at any point of time without caring if the call is already complete or not.
The second gist is definitely cleaner and helps in structuring your code better. You don’t have to bother about subscribing early too.
This is more of a clear win for the Promises based approach in my book. As I said, you can solve the same problem with an Event based approach as well but designing an Event registry just to solve the issue of multiple callbacks would be an overkill. Events by nature bring more to the table like propagation, default behavior etc. If your app requires them for better reasons, use them by all means for multiple callbacks too. They provide a viable solution. However, if you are trying to solve just the issue of multiple callbacks, Promises do it pretty well.
To summarize my thoughts, I think Promises are a nice pattern but nothing too special. Similar functionality can be mocked in other approaches too. However, if you decide to use a Promises implementation, make sure you are strictly documenting your code as otherwise it becomes an organizational problem to bring new developers on board with your codebase.
As a test exercise, I decided to mock JQuery’s implementation of its Deferred object and Promises. If you are interested in looking on how a Promises API can be implemented, my attempt is publically available here: https://github.com/lifeinafolder/Promises