I'm reading this article and the section on the promise abstraction seems a little overly complicated to me. The following is given as an example:
requestSomeData("http://example.com/foo") // returns a promise for the response
.then(function(response){ // ‘then’ is used to provide a promise handler
return JSON.parse(response.body); // parse the body
}) // returns a promise for the parsed body
.then(function(data){
return data.price; // get the price
}) // returns a promise for the price
.then(function(price){ // print out the price when it is fulfilled
print("The price is " + price);
});
It seems to me that the following could provide the same result with fewer lines of code:
requestSomeData("http://example.com/foo")
.requestHandler(function(response){
// parse the body
var data = JSON.parse(response.body);
// get the price
var price = data.price;
// print out the price
print("The price is " + price);
});
While it is true that both will ultimately accomplish the same thing, the difference is that your second example is not asynchronous. For example, consider what happens if
JSON.parse(...)
turns out to be an extremely expensive operation; you'll have to hang until everything's finished, which may not always be what you want.That's what promises get you: the powerful ability to defer the computation of the right answer until a more convenient time. As the name suggests, the construct "promises" to give you the result at some point, just not necessarily right now. You can read more about futures and promises work on a larger scale here.
The second snippet is vulnerable to denial of service attack because example.com/foo can just return invalid json to crash the server. Even empty response is invalid JSON (though valid JS). It's like
mysql_*
examples with glaring SQL injection holes.And the promise code can be improved much as well. These are equal:
And:
If we wanted to handle the error, then these would be equal:
and:
One might also add that the advantage of the first version over the second is that it separates different operations in the refinement chain (the functions don't have to be written in-place either). The second version mixes both the low-level parsing with application logic. Specifically, using the SOLID principles as guidelines, the second version violates both OCP and SRP.
Let's compare the promise example to a pure Javascript example:
As Norbert Hartl pointed out, JSON.parse() will hang the browser for large strings. So I used setTimeout() to delay its execution (after a pause of 10 milliseconds). This is one example of Kris Kowal's solution. It allows the current Javascript thread to complete, freeing up the browser to present DOM changes and scroll the page for the user, before the callback runs.
I hope the commonjs promise framework also uses something like setTimeout, otherwise the later promises in the article's example will indeed run synchronously as feared.
My alternative above looks pretty ugly, with the later processes requiring further indentation. I restructured the code, so that we can provide our process chain all in one level:
I was hoping to demonstrate that traditional forward-passing of callbacks in Javascript is pretty much equivalent to promises. However after two attempts I appear to have shown, with reference to the neatness of the code in the original example, that promises are a far more elegant solution!