Let us dive a little deeper into promises and asynchronous programming. If you already understand how asynchronous programming works in javascript, you can jump to the promises section. However if you want to under the history behind why promises exist, then read on. Javascript Promises are a much needed feature to have. They address a problem known as callback hell, which one is bound to face if they do any kind of asynchronous programming. As a web developer, this is something you cannot run away from as the browsers internal operations are asynchronous. Asynchronous here means more than one thing is happening at a time within a browsers engine. You might also hear the term synchronous in a similar manner. It means one thing happening at a time. For example, you may have heard that javascript is synchronous. This means that when executing javascript you cannot execute multiple statements at a time. Every statement that begins to execute MUST complete before another is run. This is why you hear that javascript is single threaded. Code execution is run line by line.
Other languages like Java have the ability to run multiple commands at a same time by delegating them to a different memory on the system (known as multi threading). By default this asynchronous functionality comes out of the box with the language. So how does javascript do asynchronous programming? Let me try clarify that here. You see, within a browser, many things are happening. There are many engines running simultaneously within it. The javascript engine is one of them. Another might be the rendering engine, which displays HTML document and images. The browser also has networking capabilities. So when you decide to make HTTP requests, it uses this component found in the browser to make that request. Check out how the browser works here. Ok, so now u see that everything going in the browser is asynchronous. But within the javascript engine itself, everything is synchronous.
Javascript however has hooks into browser, making it capable of communicating with it. Depending of what app you are writing, you may have to delegate some tasks so that the browser handles them as the javascript code continues. Like “Hey browser, use your HTTP request component to fetch me some data”. When javascript asks for something from the browser and it gets returned, that answer is put in what we call the “Event Queue”. This queue stores notifications of events that happen in the browser (like clicks, mouse events, key strokes, and callbacks from other async tasks, etc). So if you wanted a function to run after a user clicked on that button, that function or callback would get added to this queue when event happens. Remember clicks are not part of javascript, a click is a browser event! Javascript has what we call the execution stack. Imagine this stack is what runs every line of code you write. So in your code if you execute a function, it gets added onto this stack. When that code is done executing, it is popped off the stack, and the next line of code is run. Javascript will run everything on that stack, and when done will it turn to the event queue. If anything is in the queue its starts running those tasks, one by one until its empty. This is a continuous cycle called the event loop. So you see, its not javascript that is asynchronous, its the browser that is putting things in the event queue so that javascript can attend to it can. It is also worth noting, that when the callback is added to the queue there is no guarantee how long it will have to wait. How long it takes the current code(on the execution stack) to run to completion, and what else is already in the queue controls the time.
The Callback – Asynchronous Javascript
The cornerstone of asynchronous programming in javascript are callbacks. A callback is a function that is delegated to another function or piece of code, to run. This might seem a mouthful but is very simple… for example we can tell a function, after you do your task of fetching some data, display it. Displaying the data will be the callback function. Another example is clicking a button. When you click the button, show an alert box. The click event receives a callback function as to what to do when a click is received. As a final example, javascript has a foreach method for array objects that execute a function for each item iterated in the array. Here is the callback in action again. Callbacks can be used to manage the order in which code gets executed if thought about properly. Without the ability to write synchronous code, things such as network traffic, long running processes, etc. can potentially block parts of our code. Imagine everytime we make a request for data, we have to wait until the data comes back before moving forward with what we are doing. Not cool. Callbacks allow us to continue doing what we are doing and they get executed when those long tasks are complete.
Callbacks in Action!
Lets begin looking at some code. I mentioned earlier that events are added to a queue. Lets create a simple click event as it demonstrates a very simple idea of asynchronous programming. When you click on a button, something should happen (callback gets fired).
var button = document.getElementById('sButton');
button.onClick = function(event){
console.log("the button was clicked");
};
When the button is clicked, something interesting happens. The callback function is pushed onto this event queue(to the back of it). If the current execution stack gets emptied (all tasks get run), it will jump into the event queue and begins executing callbacks there. Can you think of what will happen if the button is clicked before the event is attached? Yes, it will not fired! This will probably not happen for such a simple user interactions. But you can begin to imagine that things can get complicated if we decide to nest or chain asynchronous calls. The order in which things happen become more and more important. Lets look at another example that really demonstrates that callbacks are put on a back burner (event queue)
function performLongTask(){ var ms = 4000 + new Date().getTime(); while( new Date < ms ); console.log('Long Task Finished'); } function clickHandler(){ console.log('Something was clicked!'); } //listen for the click event document.addEventListener('click', clickHandler); performLongTask(); console.log('Finished Executing');
We have a function called ‘performLongTask’ that performs some task for 4 seconds (yes very long indeed). We wire-up a click event on the document and run the long task function. What do you think will happen? You have go ahead and run this in your browser. Because the long running task takes 4 seconds, that will give you enough time to click anywhere on the document body. After 4 seconds, the console outputs the results. It may or may not surprise you BUT the button event appears last! Its almost as if you clicked on the button last. But no, you clicked on the button whiles the long task was performing. When you did that, the callback was put on the event queue. Only after all the javascript running on the execution stack was done did the callback function execute. Next example.
var http = require('http');
http.get('http://www.scriptonitejs.com', function(){ console.log('got a response');});
console.log('all done here');
The http.get call, it is delegated to the browser. When the task is completed, it is passed back to the javascript event queue to handle. An important quick remember here. If something is in the event queue, and there is javascript currently running, the callback in the event queue will not fire until everything in the execution is run.
With that in mind, looks look at something a little trickier. In the project files for this chapter ( folder eight ), locate xhr-tmeout.js. The contents are located below
var myXHR = new XMLHttpRequest(); myXHR.open('get', 'data.json', true); myXHR.send(); // Scenario #1 function performLongTask(){ var ms = 4000 + new Date().getTime(); while( new Date < ms ); } performLongTask(); function xhrListener(){ console.log('XHR Event has been handled'); } myXHR.addEventListener('load', xhrListener); myXHR.addEventListener('error', xhrListener); // End Scenario #1
Lets see what is going on here. We have a scenario. We make an Ajax request to get that data. Then we start our long task we saw earlier on. We perform that task for 4 seconds then we register our Ajax event handlers to register for when the request loads or fails. What do you think will happen? Will the load event ever fire? It turns out that…. YES the load will fire every single time. Can you think of a reason why from everything you have learned so far? After the send function on the xhr is run, execution of javascript continues. Remember javascript is synchronous. When you make an Ajax request, javascript send that request to the browser and continues executing the rest of its code. In doing so, EVERYTHING in the execution has to finish running. And this included registering the Ajax event handlers.
Lets look at how to cause a true delay so that the execution is paused after the send command.
var myXHR = new XMLHttpRequest(); myXHR.open('get', 'data.json', true); myXHR.send(); //call setTimeout setTimeout(function delayed(){ console.log('setTimeout started!') function xhrListener(){ console.log('XHR Event has been handled'); } myXHR.addEventListener('load', xhrListener); myXHR.addEventListener('error', xhrListener); }, 4000);
Now after the send method is called, the setTimeout will actually wait 4 seconds, before it registers the other functions. In the other case javascript continues running after the send command. In this case, there is a pause which then registers everything after 4seconds. Note that the Ajax request gets added to the event queue. Because nothing is running, the file will be loaded. If we register a load after this, then it will not work. What we are interested in has already happened.
It is important to understand how the internal operations of the browser and javascript work. With this knowledge you are able to write application that response timely to your requests.
The callback pattern.
The introduction of node came the rise of the callback pattern. If you are a node developer a lot of code you write involves using this pattern. You call a function passing it a callback, which get executed in response to events. It is quite similar to the event model(like listening for a click), but in this pattern the function is passed as an argument. Your callback might be waiting for data to load, or a response from an http request. It is worth to say node is also single threaded, however it uses what we call non-blocking techniques. Rather than doing everything one command at a time you create a callback function that gets invoked when your operation is successful or fails. Lets look at some node code that uses this pattern.
var fs = require('fs'); var fileContent = "Data to be written to file"; fs.writeFile("kofi.txt", fileContent, function(err) { if (err) { throw err; } }); console.log("Done Executing");
What we are doing here is very simple. We are require the node file system module. This will enable us to read and write data to the file system (it is like using import in other languages). We use the writeFile method to write/create a new file in our working directory. We call this file called ‘kofi.txt’. We then pass a callback to our writeFile method. As you already know this will simply execute that function once the file is written. All we do in our callback is to console log a successful message. Lets kick this up notch. How about instead of using console log, we use native node methods to reads the file after the file is written to disk. This means that when the writeFile is successful, we read that content. Like this
var fs = require('fs'); var fileContent = "Data to be written to file"; fs.writeFile("kofi.txt", fileContent, function(err) { if (err) { throw err; } //nested callback fs.readFile("kofi.txt", function(err, fileContent) { if (err) { throw err; } console.log("About to Read Contents:") console.log(fileContent); }); }); console.log("Done Executing");
See what just happen? We have nested another callback within our successful call! And why not? We do want to read that file and do something with it. How about, when we read the file write that content into yet another file. Note here that the possibilities are endless. If we did that our code would look like this
var fs = require('fs'); var fileContent = "Data to be written to file"; fs.writeFile("kofi.txt", fileContent, function(err) { if (err) { throw err; } //nested callback fs.readFile("kofi.txt", function(err, fileContent) { if (err) { throw err; } console.log("About to Data Contents:") console.log(fileContent); fs.writeFile("kofi-two.txt", fileContent, function(err) { if (err) { throw err; } console.log('kofi-two successfully written to disk'); }); }); }); console.log("Done Executing");
By now it is evident you can see how ugly this can be. Ladies and gentlemen, here we have what is called callback hell! Code like this can be hard to understand. Just imagine how this will get once complexity within your application starts growing. Imagine doing something like firing off 3 async calls that there are conditions in which those calls must be handled. This is why we have javascript promises. They give you a way to organize your callbacks that are easier to maintain. Enough talk… lets get into javascript promises.
Javascript Promises
So you see why a better solution is needed for asynchronous programming in javascript. What exactly is a javascript promise? A javascript promise is simply an object that is used as a placeholder for an asynchronous operation. We have talked about many such operations, like when we create or read a file on a disk, or make an HTTP request in a browser, etc. When we do this we are running an asynchronous function that takes in a callback to be executed when completed. The moment we call an async function(operation), we immediately return what we call a promise. When the operation is done this promise will be resolved or rejected. Before using javascript promises in its truest sense… consider how this translates to using callbacks. Lets consider a mkdir function.
mkdir("my_project_folder", success, fail);
Lets say, that mkdir is an asynchronous operation used to make directories in a web application. In the code above mkdir takes two callback functions (success and fail) INSTEAD of immediately creating the directory folder( remember its an asynchronous operation ). Traditionally this is how we do it with callbacks. But what if we change the internal operations of mkdir, so that it would return an object (promise) that you could then attach your callbacks to? That is the idea behind javascript promises 🙂
var promise = mkdir("my_project_folder");
Now the promise returned from mkdir is assigned to our promise variable so we can attach our callbacks to it. It is worth knowing that there are life cycles that a promise object goes through. Before the async operation is complete, the promise is said to be in a pending state. Though if the operation has not began, its also said to be pending. Once the operation completes, the promise is settled, however it enters yet another state of whether the operation was successful or it failed. If successful, we say the promise is in a fulfilled state. If it failed then it has a state of rejected. Once the state of a promise has been fulfilled, it cannot be changed.
So back to our mkdir function, imagine that a promise is returned, we attach our callbacks by using the then() method on our object. Something like this
promise.then(function(dirName){
//perform success/fulfillment functionality
}, function(err){
//perform failed/rejected functionality
});
The .then() method of a promise object takes two parameters. They are handlers for a fulfilled and rejected state, respectively The promise object also has a catch method for handling errors. You would normally see it chained together with .then(). Its best to always try to handle errors in your application. If you don’t do this, you could potential get an error in your app and not detect it.
promise.then(function(dirName){
//perform success/fulfillment functionality
}, function(err){
//perform failed/rejected functionality
}).catch(function(err){
//perform failed/rejected functionality
});
So we have a promise returned from mkdir, but internally what does that function look like? Lets take a quick peek under the hood.
function mkdir(dirName){
//create a new promise object from the Promise constructor
var promise = new Promise();
//return the promise
return promise;
}
The mkdir function creates a promise object from the global promise constructor, then returns it. Its that simple. But, there is more to this Promise constructor. Lets look at that. A promise constructor expects a callback known as the resolver function. This function receives two arguments (resolve and reject).
function mkdir(dirName){
//create a new object from the Promise constructor
var promise = new Promise(
function resolver(resolve, reject){
//perform some functionality here
//for some object called make_dir
make_dir.onSuccess = function(){
resolve(make_dir);
};
make_dir.onFail = function(){
reject(make_dir);
};
}
);
//return the promise
return promise;
}
Can you see where this is going? 🙂 Look at arguments that are sent to the resolver function. Resolve is called when the operation is successful resolve(make_dir) and Reject is called when it fails. So its the resolver thats really doing the magic. In the example i have onSuccess and onFail. We are assuming that these are methods that exists on the object (for example if it was an image object, such a method would be onLoad). So when those events are fired by the object, then we can finally handle our success or failure.
var promise = mkdir("my_project_folder");
promise.then(function(){
//fulfillment
//resolver will run this functionality
}, function(){
//rejection
//resolver will run this functionality
});
Another useful tip is that you can save your promise for later use. Think reusability here. Imagine that you had an application that grabbed data externally. Lets just say it returns some kind of user account information after an async operation. We could save this data in an object. Within that object, we have access to thinks like their balance, age, profile info, etc. We can cache this promise object in our application, and use it throughout our app as needed. Once cached, its available for us to use. We could create a helper method that can be accessed at will.
The promise constructor
Lets dive a little bit deeper into the promise constructor and how it is used on another level. Earlier on i spoke about the event queue and how that works. Promises when resolved or rejected, also get put into this queue. Remember the the resolver takes in two callbacks (resolve and reject). When any of them are called, they are added to the queue. Since this operation is asynchronous, they are added to the queue when certain conditions are met. You can invoke a promise to resolve or reject by executing those callbacks immediately. The way we have done it so far is to wait for some kind of event to fire then we resolve or reject the project, for the most part this is what you want to it. But lets give ourselves more power and invoke them ourselves in the promise constructor.
var myPromise = new Promise(function(resolve, reject){
console.log("my promise to you");
resolve();
});
Let take a look at this slowly. We know that our declaration above returns a promise object immediately. We can even tell if we run this in the solve, we notice that “my promise to you” in executed. The resolve function does not run though. We have not yet written that functionality (if you comment it out, we still get the console message). Lets tell it what to do
myPromise.then(function(){
console.log("..promise resolved");
});
In the console, we first see “my promise to you”, then “..promise resolved”. Do you know why? Because the promise is first returned immediately (that execution runs as it returns the promise). When we resolve the promise, it is added to the queue. If all currently running code is done, then only will the resolve function fire. See you can tell what the execution order will be for this piece of logic
var myPromise = new Promise(function(resolve, reject){
console.log("my promise to you");
resolve();
});
myPromise.then(function(){
console.log("..promise resolved");
});
console.log("test1");
console.log("test2");
console.log("test3");
console.log("test4");
console.log("test5");
Did you figure it out right away? Yep! Our output looks like this
my promise to you
test1
test2
test3
test4
test5
..promise resolved
Because resolve is added to the queue, it doesnt first right away until all running tasks are done. With this knowledge, you can do some pretty exciting stuff as you have a better understanding of how and when you can resolve asynchronous operations.
Its important to note, that once you resolve or reject a promise, you have changed the internal state of it and it cannot be changed again. So in our first sample, if we executed reject as well as resolve, reject will never be called.
var myPromise = new Promise(function(resolve, reject){
console.log("my promise to you");
resolve();
reject(); // this will never be called});
reject() will never be called because the state of the promise has been fulfilled. This is a good thing, as you would not want the state to change once settled (as resolved or rejected) to prevent race conditions ( other resolver functions running to possibly resolve or reject again, no no! ).
Sometimes you want your promise to represent just a single value. This way when resolved or rejected, that value is passed to the callback function.
var promise = Promise.resolve("My String Param");
or use the constructor
new Promise(function(param){
console.log(param)
});
This also applies if you wanted to pass a rejected parameter. In the above code samples the resolved or rejected callbacks already have the passed in values. Pretty neat.
Now here is something new and interesting i want to try and explain, remember that Promise.resolve(value) returns a Promise
object that is resolved with the given value. If the value returned is a thenable (meaning it has a then
method), the returned promise will “follow” that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value. Generally, if you want to know if a value returned is a promise or not – Promise.resolve(value)
it instead and work with the return value as a promise. So now lets create a thenable and pass it to our promise.
var thenable = {
then : function(resolve, reject){
resolve("my param");
}
};
Lets pass the thenable to our resolve.
var myPromise = Promise.resolve(thenable);
myPromise.then(function(param){
console.log(param)
});
Lets see what happens. The Promise.resolve will call the thenable.then(). Whatever is returned is passed to Promise.resolve(). We have already seen what happens when passed to resolve(). It returns “my param”. We can now use that value in the myPromise variable. Which also returns “my param”. Isn’t that beautiful? 🙂
So whats the use case for this? Well, what if you were expecting a promise from say a different library or API? Sometimes you dont know if what is being received has a then which needs to be run before doing anything. This approach is what helps us in such situations!
Whew, we have barely scratched the surface when it comes to javascript promises. I am going to stop here, but stay tuned for part 2!!
Feel free to reach out on twitter as well ( @scriptonian ) if you have any questions.
Thanks for your article…
Thanks for Reading 🙂