Mastering JavaScript Promises - Sample Chapter
Mastering JavaScript Promises - Sample Chapter
P U B L I S H I N G
pl
C o m m u n i t y
$ 49.99 US
32.99 UK
Sa
m
Muzzamil Hussain
This book is for all the software and web engineers wanting
to apply the promises paradigm to their next project and get
the best outcome from it. This book also acts as a reference
for the engineers who are already using promises in their
projects and want to improve their current knowledge
to reach the next level. To get the most benefit from this
book, you should know basic programming concepts, have
a familiarity with JavaScript, and a good understanding
of HTML.
Mastering JavaScript
Promises
ee
D i s t i l l e d
Mastering JavaScript
Promises
Discover and explore the world of promises, one of JavaScript's
most powerful concepts
E x p e r i e n c e
Muzzamil Hussain
technology for more than a decade now. During this period, he developed a wide
range of software products and services for companies around the world.
He completed his Digital Communications Networks course from the prestigious
London Metropolitan University, London, United Kingdom. He is skilled in crafting
software products and services with the best of technical and management skills,
which he has acquired over the past 14 years.
The best part of his wealth of experience is the dual skill sets of project management
and software engineering, which is a paramount feature of his work.
Muzzamil has worked with some of the big industries based in Pakistan, the UK,
the USA, Canada, and UAE. These days, he is associated with Systems Limited, the
first and one of the oldest software companies in Pakistan, in the capacity of senior
project manager.
He also provides consultancy to start-ups on the software development life cycle,
project management, release engineering, and continuous integrations.
He is also heavily involved in experimenting with the latest technologies such as
the MEAN stack, an opinionated full-stack JavaScript framework, and Apache
Hadoop for the distributed processing of large data sets.
He blogs at http://muzzamil.net/.
Preface
In this book, we will explore the concept and implementation of promises in
JavaScript. This book has an evolving context that will lead you from a beginner's
level to the master level of promises. Every chapter of this book will give you an
outline to achieve a specific goal that will help you realize and quantify the amount
of knowledge you absorb in every chapter.
The entire stack of chapters is designed in a way such that the book will evolve as
you go through it. Every chapter in this book is designed in two parts: one is the
concept building part and the other is the experimenting part, where you will be
able to sample snippets of concepts, sometime in code, sometimes in best practices,
and sometimes in images.
The first four chapters are more or less like theoretical knowledge to provide you
with a solid foundation on JavaScript and promises. So, if you're a novice and don't
know anything about JavaScript or promises, you will learn a great deal with these
chapters. The rest of the chapters are more technology-oriented and you will learn
implementation of promises in WinRT, Angular.js, jQuery, and Node.js. So, if you
are a professional and already have some idea of promises, you may jump right into
Chapter 5, Promises in WinRT, but I'd prefer it if you read through all the chapters for
a better understanding of this book.
We will start with the introduction to JavaScript and how it has seen ups and downs
from the late 90s up to the first decade of the twenty first century. We will focus
on what asynchronous programing is and how JavaScript is using it. Moving on,
I will introduce promises and its impact and how it's implemented. To make the
book interesting and impart more knowledge to you, I will show you how promises
has made its place in the heart of Java, one of the most mature object-oriented
programming languages. This add-on content will act as a detour and clarify
concepts in a more efficient way.
Preface
The flow of book will then lead you to the implementation of promises in some of the
most used JavaScript libraries. We will see a sample code on how the mechanism of
these libraries work. Finally, we will wrap up the book with our last chapter that will
show you what is coming next in JavaScript, why it has gained so much attention
over the past few years and what would be the possible future of JavaScript.
Preface
Promises in Node.js
In the previous chapter, we learned about promises in WinRT and how they are
implemented using the Microsoft platform. The promises concept has had wider
coverage than other languages. This is one of the fastest growing concepts in open
source technologies.
In this chapter, we will discuss an implementation of JavaScript that is dramatically
changing the course of modern web development and enhancing our ways to
real-time web. This amazing piece of technology is called Node.js, a platform written
in JavaScript and based on the V8 Engine by Google. Promises in Node.js are far more
interesting, evolving, and productive than any other platform or implementation can
offer. Let's dive into the details and find out what promises in Node.js have to offer
for real-time web.
[ 75 ]
Promises in Node.js
Google Chrome
Request 1
Interpreter / compiler
Machine code
Request 1
Request 1
Request 1
Request 1
[ 76 ]
Chapter 6
Many developers started exploring the code base to find the possibilities of
solutions they were involved in and to get the most out of this new amazing
compilation of codebase.
Ryan Dahl was among those developers who wanted to give a shot to V8 JavaScript
engine as he was busy trying to solve a problem while working at Joyent. The
problem was making a browser know how much time is left for an upload process.
Inspired by the recent release of V8 and Ruby's Mongrel web server, he drafted the
codebase that later evolved into Node.js.
[ 77 ]
Promises in Node.js
Choice of environment
Node.js is platform independent in a way that it has all the installations, setups,
and libraries available for all the major operating systems currently available.
It's available for all Unix-based operating systems, as well as Mac and Windows
platforms. Since our prime focus here is to make you understand what the link is
between Node.js and promises, we will base our code examples on the Windows
7 (any edition) platform since it's widely available and Node.js is also available for
Windows 7 in stable conditions. Plus, it's very simple and less time-consuming.
Please remember that using Windows-based systems won't make any
difference to code and their outputs. This will remain the same for every
operating system with no change of any kind. You can easily use the
same codebase on any other operating system without any hesitation.
[ 78 ]
Chapter 6
Please note that the current version of Node.js is 0.10.31 and the current version
of NPM is 1.4.23. Our examples will be based on these versions, not lesser than
these versions.
Promises in Node.js
response.end('Hello World\n'); // this is the browser's output.
}).listen(1337, '127.0.0.1'); // 1337 is the port number where the
browser is listing to this request.
console.log('Server running at http://127.0.0.1:1337/'); //This
line will show at command prompt
Save the file by any name with the .js extension. For our example, we use the name
server_example.js. Save this file in a directory (such as Promises_in_Node) and
open your terminal program. For Windows, it will be Command Prompt. Navigate
to the directory where you have saved your file and type in the following command:
If the code has no errors, it will compile and show the following output on the screen:
Now, open Chrome and type http://127.0.0.1:1337/ in the address bar and hit
Enter. This screen will show you the successful output from the Node.js server:
That's it! You are now ready to take a deep dive in to promises in Node.js.
Chapter 6
The following sections will let you understand more about Node.js and promises
and how promises are gaining so much respect from Node.js developers.
Messy, isn't it? Not only is it messy, but also very confusing and hard to maintain.
Now, look at the following code using promise:
Q.fcall(process_one)
.then(process_two)
.then(process_three)
.then(process_four)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Error Handler
})
.done();
[ 81 ]
Promises in Node.js
Now, this code is less confusing, more productive, and it has one additional quality,
which is an implicit error propagation like we have try-catch and finally blocks
in Java that catch any unwanted exception and save the program from crashing
totally when encountered with an unexpected condition.
The callback approach is known as the inversion of control, a function that is capable
of accepting a callback rather than returning a value. Such a mechanism can more
easily be described as the phrase, Don't call me, I will call you.
Promises in Q have a very special tendency as it's clearly made independent the input
arguments from control flow arguments. One can only be able to see its true benefits
when using and creating APIs, particularly, variadic, rest, and spread arguments.
As you can see, the prompt says its q at version 1.2.0, which is stable and also
backward compatible. We will use this release for all our examples in this chapter.
With this installation and past upgrades in our environment, we are now able to
sample some of the common yet fruitful features that promises give us in Q.
Promises have a then method, which you can use to get the eventual return value
(fulfillment) or throw an exception (rejection). By now, we all know it, after reading
the previous chapters of this book.
iPromiseSomething() .then(function (value) { //your code },
function (reason) { //your code } );
Chapter 6
As you can see, the resolution of a promise is always asynchronous, which means the
fulfillment or rejection handler will always be called in the next turn of the event loop
(that is, process.nextTick in Node.js). This mechanism ensures that it will always
return a value either before the fulfillment or rejection handlers are executed.
Propagation in Q
The then method always returns a promise that will either be handled or rejected.
In our example code, we assign the output to the reapPromise variable, which will
hold the value:
var reapPromise = getInputPromise()
.then(function (input) {
}, function (reason) {
});
The reapPromise variable is the new promise for the return value of either handler.
Only one handler will ever be called and it will be responsible for resolving
reapPromise as a function, which can only either return a value (a future value) or
throw an exception.
Whatever is the case, there will be the following possible outcomes:
If the getInputPromise() promise gets rejected and you forget the rejection
handler, the error will go to reapPromise:
var reapPromise = getInputPromise()
.then(function (value) {
});
If the input promise gets fulfilled and you fail the fulfillment handler, the value
will go to reapPromise:
var reapPromise = getInputPromise()
.then(null, function (error) {
});
[ 83 ]
Promises in Node.js
When you are only interested in handling the error, Q promises to provide a
fail shorthand:
var reapPromise = getInputPromise()
.fail(function (error) {
});
If you are writing JavaScript for modern engines only or using CoffeeScript,
you may use catch instead of fail.
Promises also provide a fin function that is like a finally clause. The final handler
gets called, with no arguments, when the promise returned by getInputPromise()
either returns a value or throws an error.
The value returned or error thrown by getInputPromise() passes directly to
reapPromise unless the final handler fails, and may be delayed if the final handler
returns a promise:
var reapPromise = getInputPromise()
.fin(function () {
});
In short:
The eventual value or error has the same effect as an immediate return value or
thrown error; a value would be ignored, an error would be forwarded.
So when we are looking for propagation, we need to keep in mind what we want
to see from our returning value. The then, fail, and fin functions are the keys to
remember while using propagations in Q.
If you are writing JavaScript for modern engines, you may use
finally instead of fin.
[ 84 ]
Chapter 6
So, we can say that the ANY_promise() function can contain some behavior, and this
will return a promise object that leads to eventually return a result. As soon as the
real result is returned, it will set off the next function in the chain.
This looks good now, what if you want to set off an asynchronous function and wait
until we get a result before executing the behavior of the next promise in the chain?
Q has a solution for this. Using .defer() and deferred.resolve(), you can get it
in a much more manageable and predictable manner.
Sequences in Q
Like chaining, sequences is another way to stage your result in the way you want.
Sequence is the way you can use in a predefined manner to get the outcome
of the situation as desired. To hold it more tightly and to generate the result,
Q provides sequences in a unique way.
Suppose you have a number of promise-generating functions, all of them need to
be run in a sequence. You can do it manually like this example:
return seq(startValue).then(secondValue).then(thirdValue);
You have to make sure that every then() must be in a sequence with another
then(); to maintain the sequence. Failing to do so will break the sequence,
and you will not be able to get another value later.
[ 85 ]
Promises in Node.js
The other way is to instruct your sequence dynamically. This can be faster but
needs more attention while executing the code as unpredictable code may harm the
overall sequence.
Here is a snippet of how you can do it dynamically:
var funcs = [startValue, secondValue, thirdValue];
var result = Q(startValue);
funcs.forEach(function (f) {
result = result.then(f);
});
return result;
If this looks like you are using too many lines of code, use reduce:
return func.reduce(function (tillNow, f) {
return tillNow.then(f);
}, Q(startValue));
Combination in Q
With Q, you have a unique facility in Node.js to write cleaner and manageable code
if you want to combine a list of array of promises. This can help you to write a more
complex level of sequential data structure in a more manageable way. How can we
get there? Use all. Consider the following example:
return Q.all([
eventAdd(2, 2),
eventAdd (10, 20)
]);
The Q.all([func1(), func2()]); function will be the generic form of the preceding
code. You can also use spread to replace then. Can we replace another new thing
with Q? Not really! The spread function spreads the values over the arguments of the
fulfillment handler. For the rejection handler, it will get the first signal of failure. So,
any of the promises destined to fail first will get it handled by a rejection handler:
function eventAdd (var1, var2) {
return Q.spread([var1, var2], function (var1, var2) {
return a + b;
})
[ 86 ]
Chapter 6
}
Q.spread(). Call all initially
return getUsr() .then(function (userName) { return [username,
getUser(userName)]; }) .spread(function (userName, user) {
});
When you call the function, it will return allSettled. Within this function, a promise
will be returned as an array that holds the value. When this promise has been fulfilled,
the array contains the fulfillment values of the original promise within the same
sequence as those promises. The beauty is, if any promise is rejected, it will be rejected
immediately, not waiting for the rest of others to come and share their statuses:
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
The any function takes in an array of promises to return a promise that is fulfilled
by the first given promise to be fulfilled, or rejected, provided all the given promises
were rejected:
Q.any(promises).then(function (firstPromise) {
// list of any of the promises that were fulfilled.
}, function (error) {
// All of the promises were rejected.
});
[ 87 ]
Promises in Node.js
Let's have a look at the following snippet and see how it can be handled:
return scenario().then(function (value) {
throw new Error("I am your error mesg here .");
}, function (error) {
// We only get here if scenario fails
});
Why is this case happening? Suppose the parallelism between promises and
try/catch and while we are trying to execute scenario(), the error handler
represents a catch for scenario(), while the fulfillment handler represents
code that happens after the try/catch block. Now, this code needs its own
try/catch block.
The try/catch block is not a new concept for all of you who have written code for
some major languages. Since Node.js is based on JavaScript and Q is handling it at
the moment, the syntax might be a bit different but the functionality is more or less
the same like the following code:
Q.
try(function()
{return scneario().then(function(value)throw new Error("im
your thrown error");)} )
.catch({ function (error)
{console.error("i am catched",error)}
});
Simply put, in terms of promises, it means you are chaining your rejection handlers.
Chapter 6
There are more than enough advantages of using Q. For this specific topic,
it provides us a short call progress which minimizes our effort to only one line
using *.progress();.
return uploadFile().progress(function (progress) {
// We get notified of the upload's progress
});
Why are we doing this? Why do we need to invoke the mechanism like this? The
answer is very simple, you have to end the chain or have to return it to another
promise. This is due to the fact that since handlers catch errors, it's an unfortunate
pattern that the exceptions can go unobserved.
Every once in a while, you will need to create a promise from scratch. This is quite
normal; you can either create your own promise or get it from another chain.
Whatever the case is, consider that it's a beginning. There are a number of ways in
which you can create a new promise using Q. Here are some of them:
Q.fcall();
//Using this function fcall you can create and call other //
functions, along with Promise functions. To do that simply //follow
this syntax
return Q.fcall(function () {
return 10;
});
[ 89 ]
Promises in Node.js
Not only this, fcall(); can also be used to get an exception-handled promise that
looks like the following snippet:
return Q.fcall(function () {
throw new Error("I am an error");
});
Since fcall(); can call functions, or even promised functions, this uses the
eventualAdd(); function to add two numbers:
return Q.fcall(eventualAdd, 2, 2);
[ 90 ]
Chapter 6
Q.delay()
Q.notify()
deferred.notify()
The preceding functions are not only able to create delays when required but
also notify when the delay is likely to occur. If you want to defer the notification,
deferred.notify() will serve the purpose.
Q.delay()
The following code is a simplified implementation of Q.delay:
function delay(ms) {
var deferred = Q.defer();
setTimeout(deferred.resolve, ms);
return deferred.promise;
}
Q.timeout()
A simple way to work with Q.timeout:
function timeout(promise, ms) {
var deferred = Q.defer();
Q.when(promise, deferred.resolve);
delay(ms).then(function () {
deferred.reject(new Error("Timed out"));
});
return deferred.promise;
}
[ 91 ]
Promises in Node.js
deferred.notify()
Finally, you can send a progress notification to the promise with
deferred.notify().
There is a wrapper for XML HTTP requests in the browser:
function requestOkText(url) {
var request = new XMLHttpRequest();
var deferred = Q.defer();
request.open("GET", url, true);
request.onload = onload;
request.onerror = onerror;
request.onprogress = onprogress;
request.send();
function onload() {
if (request.status === 200) {
deferred.resolve(request.responseText);
} else {
deferred.reject(new Error("Status code was " +
request.status));
}
}
function onerror() {
deferred.reject(new Error("Can't XHR " +
JSON.stringify(url)));
}
function onprogress(event) {
deferred.notify(event.loaded / event.total);
}
return deferred.promise;
}
Chapter 6
console.error(error);
}, function (progress) {
// Log the progress as it comes in.
console.log("Request progress: " + Math.round(progress * 100)
+ "%");
});
function onload() {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(new Error("Status code was " +
request.status));
}
}
function onerror() {
reject(new Error("Can't XHR " + JSON.stringify(url)));
}
function onprogress(event) {
notify(event.loaded / event.total);
}
});
}
Promises in Node.js
Static methods of Q
Typecasting of promises objects is a must and you must have to convert promises
generated from different sources in Q type promises. This is because of the simple
fact that not all promise libraries have the same warranties as Q and certainly don't
provide all of the same methods.
//using when
return Q.when(AmIAvalueOrPromise, function (value) {
}, function (error) {
});
//The following are equivalent:
return Q.all([a, b]);
return Q.fcall(function () {
return [a, b];
})
.all();
Most libraries only provide a partially functional then method. Q, on the other
hand, is quite different to others:
return Q($.ajax(...))
.then(function () {
});
If there is any way that the promise you have got is not a Q promise as provided by
your library, you should wrap it using a Q function. You can even use Q.invoke();
as shorthand, as shown in the following code:
return Q.invoke($, 'ajax', ...)
.then(function () {
});
Promise as a proxy
One marvelous thing about a promise that distinguishes it from the rest is that
it can act as a proxy for another object, not only for local objects but also for a
remote object. There are methods that let you confidently employ properties or
call functions. All of these exchanges return promises, so that they can be chained.
[ 94 ]
Chapter 6
You can trim round-trips by using these functions instead of then() if the promise is
a proxy for a remote object.
Even in the case of local objects, these methods can be used as shorthand for
particularly-simple gratification handlers. For example, you can replace:
return Q.fcall(function () {
return [{ foo: "bar" }, { foo: "baz" }];
})
.then(function (value) {
return value[0].foo;
});
[ 95 ]
Promises in Node.js
They are both used for calling functions with the same resemblance of Node.js so
that they can generate promises.
Use Function.prototype.bind()
There is yet another way you can create reusable wrappers, using:
Q.denodeify:
//using Q.denodeify
var readFile = Q.denodeify(FS.readFile);
return readFile("foo.txt", "utf-8");
Q.nbind:
// Q.nbind
var redisClientGet = Q.nbind(redisClient.get, redisClient);
return redisClientGet("user:1:id");
[ 96 ]
Chapter 6
Q.delay(100).done(function explode() {
throw new Error("hello I am your error Stack!");
});
}
TheDepthOfMyCode ();
This will gives us a raw-looking unhelpful stack trace looking similar to this:
Error: hello I am your error Stack!
at explode (/path/to/test.js5:166)
at _fulfilled (/path/to/test.js:q:54)
at resolvedValue.promiseDispatch.done (/path/to/q.js:923:20)
at makePromise.promise.promiseDispatch (/path/to/q.js:400:23)
at pending (/path/to/q.js:397:39)
at process.startup.processNextTick.process._tickCallback (node.
js:244:9)
Unlike most of the time, in JavaScript, we use breakpoints or use alert() to see
where the error occurred, which is quite frustrating and time consuming. Q has not
only given us an elegant way to get to a point where the error is happening, but also
the entire trace can be read and analyzed to solve the problem.
In Node.js, this feature can also be enabled through the Q_DEBUG
environment variable:
Q_DEBUG=1 node server.js
[ 97 ]
Promises in Node.js
Later, you can use: httpGet(...).then(function (res) {...}); but you have
to make sure that functions return promises. The first Q.defer() returns a set of an
empty promise and operations for it. The deferred.promise is the empty promise
which fixes a certain value:
// promise-resolve-then-flow.js
var deferred = Q.defer();
deferred.promise.then(function (obj) {
console.log(obj);
});
deferred.resolve("Hello World");
This prints Hello World to the console. In general, you can transform usual
callback actions:
// promise-translate-action.js
action(arg1, arg2, function (result) {
doSomething(result);
});
To promise actions:
// promise-translate-action.js
var promiseAction = function (arg1, arg2) {
var deferred = Q.defer();
action(arg1, arg2, deferred.resolve);
return deferred.promise;
}
promiseAction(arg1, arg2).then(function (result) {
doSomething(result);
});
[ 98 ]
Chapter 6
There is another good thing about promises of Q. They have a support method of
primitive access as a promise.
By them, the decomposed actions also translate to:
// object.primitive.js
httpGet(url.parse("http://example.org"))
.get("handlers").get("location").post("replace", [/^http:/,
""])
.then(console.log);
View revisited
The view() method helps in mirroring all the values into Q-based promises without
any distinction, either it comes from a value or any other function. There are two
methods that can make this possible:
promise.post(name)
promise.send(name)
[ 99 ]
Promises in Node.js
This converts a method of the promise value to a promise of the method result.
A result of view() has methods for all methods of the promise value. You can use
view in the then callback of view(), for example:
// object-view.js
Q.resolve(new Date()).view().then(function (dateView) {
return dateView.toTimeString().then(function (str) {
return /\((.*)\)/.exec(str)[0]
});
}).then(console.log);
Aborting a promise
We saw how done(); is used earlier, but here it comes in with a total impression.
Using done();, we can conclude our promise and abort our program. I always
have a way to chain the promises:
then().then().done();
If the promise is vetted (and did not catch the error before), the done() function
forcibly spawns an uncatchable error (for example, setTimeout(function ()
{throw ex;}, 0)).
On Node.js REPL, run Q.reject("uncaught").done(), then exit with an error.
If the error reached to the done() function, you can think of it just a programming
bug (not an exception state).
Q.nfbind(fs.readFile)(filename, encoding).then(console.log);
[ 100 ]
Chapter 6
Q has more to offer, but the preceding ones are the best and most used and sensible
use of these can help us write a more manageable, cleaner, and dynamically
controlled mechanism.
Summary
This chapter was an amazing journey from start to finish, and it has taught us from
the very beginning about Node.js. We didn't opt for explaining stuff in computer
science terminology, instead we went to the mechanical part of the V8 engine, and
from there we saw how real-world objects can be mapped into computing.
We learned what Node.js is, from where this most amazing library started, who built
it, and why and how it's helping us create real-time web apps.
Then we moved to Q, the best way to offer promises to Node.js. We saw how we can
install Q and then we saw different ways of using Q along with Node.js. We have
also achieved our purpose of using Q as a promises implementation of Node.js.
This chapter will encourage you to start working on Node.js, especially on how to
take advantage of Q as the library of promises for Node.js.
In the next chapter, we will be looking in-depth in the world of Angular.js and how it
got promises implementation.
[ 101 ]
www.PacktPub.com
Stay Connected: