Promises are an alternative way to the callback system for javascript asyncronous event. Instead of calling a callback, we call a function that returns immediatly a promise. Thus we can exit very soon from the function and continue with our code as in synchronouse code style.
Promises are a way to avoid the Callback hell and they are typicaly used for Ajax Requests (jQuery returns promises by default from Ajax requests) and UI animations. With promises you can easily synchronize tasks or execute tasks sequentially.
What is a promise?
A promise represents a value that is not yet known.
A promise has 3 possible states: unfulfilled (initial state, the value is unknown), fulfilled (the promise knows the future value) and failed (there was an error).
But…Who is the promise maker? It is a deferred object responsible for the computation of the future result. This object is the only one that has the power to change the status of a promise (a promise is a read-only object).
What is a deferred?
A deferred represents work that is not yet finished.
In JQuery a deferred has a method resolve and a reject one.
In a deffered object we can attach a calback to these events: done, fail, always.
var deferred = $.Deferred(); deferred.done(function(value) { alert(value); }); deferred.resolve("hello bytes!");
Synchronize Parallel tasks with promises
For combining differents promises we can use the $.when method that will be “resolved” when all the promises are resolved or rejected if any of the promises is rejected. The done callback has the result of all the promises.
This is the base schema used to synchronize code:
$.when(task1, task2,task3).done(function () { console.log('All the tasks are finished'); });
Let’s see an example in which we have two differents function performing an ajax call:
function getPhotos(userId){ // Create a deferred object var d = $.Deferred(); // Make an Ajax-Call $.post( "/api/photos/", {json: JSON.stringify({user: userId})} ).done(function(p){ d.resolve(p); }).fail(d.reject); // Return a promise return d.promise(); } function getProfile(userId){ return $.post("/api/profile/", { json: JSON.stringify({ user: userId }) }).pipe(function(p){ return p.profile; }); } // Sync $.when(getPhotos(101), getProfile(101)) .done(function(photos, profile){ alert("Job done "); });
Build a loading message while waiting for multiple ajax requests
With the use of $.when and the help of the JQUERY BlockUI Plugin), it’s easy to show a “Loading message”:
function getPhotos(userId){ // Create a deferred object var d = $.Deferred(); // Make an Ajax-Call $.post( "/api/photos/", {json: JSON.stringify({user: userId})} ).done(function(p){ d.resolve(p); }).fail(d.reject); // Return a promise return d.promise(); } function getProfile(userId){ return $.post("/api/profile/", { json: JSON.stringify({ user: userId }) }).pipe(function(p){ return p.profile; }); } function load(){ $.blockUI({message: "Loading..."}); var loadingPhotos = getPhotos(11) .done(function(photos){ // do the job }); var loadingProfile = getProfile("11") .done(function(profile){ // do the job }); $.when(loadingPhotos, loadingProfile) .done($.unblockUI); } load();
Executing sequential tasks with promises
var stepTask1 = $.ajax(...); stepTask2 = stepTask1.then( function (data) { var def = new $.Deferred(); setTimeout(function () { console.log('Request completed'); def.resolve(); },3000); return def.promise(); }, function (err) { console.log('StepTask1 failed: Ajax request'); } ); stepTask2.done(function () { console.log('All the task has been executed in sequence'); });