The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
-MDN
Put simply[1], it is a means to reorganize asynchronous (concurrently executing) code to reduce nesting among other reasons.
Over the next couple of posts, I'm going to give a brief primer on what a promise is and how to use it but the main purpose of this article is to understand how a Promise library is implemented. I was poking through the source code of Pinkie not too long ago and it seemed like a great article candidate.
Why use one?
Without going into too much detail, they are used to avoid "Callback Hell" as well as create a more familiar code-flow (the try/catch
). pattern. Here's a contrived but pseudo real-world example:
// require statements omitted for readability and brevity.
function processData( startingData, cbResult ) {
//
// Do something with startingData and ultimatly set result to finalizedResult.
// ...
var finalizedResult = startingData;
cbResult(finalizedResult);
}
function parentMethod() {
fs.readFile('/my/made/up/file.txt', function callback1(err, data) {
if(err) throw err;
processData( data, function callback2(results) {
sendResultToServer(result, function callback3(response) {
executionOrderGetsConfusing(response);
});
});
});
}
A promise allows us to restructure the code as such as this:
//
// require statements omitted for readability and brevity.
var readFile = Promise.promisify(require("fs").readFile); //See http://bluebirdjs.com/docs/api/promise.promisify.html
function processData( startData ) {
//Return a promise which impl will use "then" to implement what was "cbResult"
return new Promise( function(resolve) {
//
// Do something with startingData and ultimatly set result to finalizedResult.
// ...
var finalizedResult = startingData;
return finalizedResult; //Returning something makes it available in chained 'then' statements.
} );
}
function parentMethod() {
readFile('/my/made/up/file.txt')
.then(function callback1(data) { //Note that (data) => {} is a newer way of defining a function in JS.
return processData( data ); //Not available everywhere yet though. Equiv. to function(data) {}
})
.then(function callback2(results) {
return sendResultsToServer(result);
})
.then(function callback3(response) {
executionOrderIsClearer(); //as well as where each method starts and ends.
})
.catch(function(err) {
// Handle errors from the readFile to callback3.
// Like wraping the above in a try/catch
});
}
There appears to be more code in the later version because, I suppose, there is. The use of a promise doesn't reduce code. It makes the flow more obvious. And I do believe that is achieved here. You now see the order of execution more clearly. Each then
statement must be completed before the previous then
statement and should an error occur, the catch(function(er){})
will intercede to allow you to handle the error gracefully.
More examples
Promises in JavaScript has become quite ubiquitous. Should you want to see more examples or practical uses of Promises, the pg-promise library has excellent up-to-date examples that are both clear and real-world. The library itself is a PostgreSQL interface for Node.
Next
In the next post, we'll look at how promise libraries are implemented by dissecting the implementation by Floatdrop called Pinkie. A small ES2015 Promise implementation.
-
An Oversimplification but suitable for our purposes. ↩︎
Top comments (0)