Promises

Asynchronous Programming Made Easy

Anshul Bajpai / @anshulbajpai

http://www.equalexperts.com

A typical Javascript callback


function readFile(filePath) {
  var filesystem = require('fs');

  filesystem.readFile(filename, 'utf8', function(err, data) {
    if (err) throw err;

    console.log('OK: ' + filename);
    console.log(data)
  });
}
					

A fullfledged callback hell

Fetch from memory


function fetchFromMemory(callback) {
  setTimeout(function() {
    callback('This is data from memory')
  }, 3000);
};					
					

Let's add some random error


function isError() {  
  return Math.floor((Math.random()*100)+1) % 2 == 0;
};

function fetchFromMemory(callback) {  
  if(isError()){
    throw "Memory fetch failed";
  }
  
  setTimeout(function() {
    callback('This is data from memory')
  }, 3000);
};
					

Fetch from remote call


function fetchFromRemoteCall(callback) {
  if(isError()){
   throw "Remote call failed";
  }

  setTimeout(function() {
    callback('This is data from remote call')
  }, 3000);
};
					

Let's stitch the components


function readWrite() {
  fetchFromMemory(function(memoryData) {
    fetchFromRemoteCall(function(remoteCallData) {
      console.log(memoryData + "-" + remoteCallData);
    });
  });  
};
					

Let's add some error handling


function readWrite() {  
  try{    
    fetchFromMemory(function(memoryData) {      
      try {        
        fetchFromRemoteCall(function(remoteCallData) {
          printData(memoryData + "-" + remoteCallData);
        });
      }
      catch(err){
        console.error(err);
      }   
    });
  }	
  catch(err){
    console.error(err); 
  }
};
					

What if your control flow instead of looking like this..


function foo(){                                        
  doThis(function(){
    doThat(function(){
      doSomeMore(function(){
        // Some more logic    
      })  
    });  
  });
};

					

starts looking like this..


function foo(){
  doThis()
  .then(function(){
    doThat();
  })
  .then(function(){
    doSomeMore();
  })
  .then(function(){
    // Some more logic	
  });
};

					

You remember this?


function readWrite() {
  try{
    fetchFromMemory(function(memoryData) {
      try {
        fetchFromRemoteCall(function(remoteCallData) {
          printData(memoryData + "-" + remoteCallData);
        });
      }
      catch(err){
        console.error(err);
      }   
    });
  }	
  catch(err){
    console.error(err); 
  }
};
					

What if we turn the code into this?


function readWrite() {
  var memoryData;

  fetchFromMemory()
  .then(function(dataReadFromMemory) {
    memoryData = dataReadFromMemory;
    return fetchFromRemoteCall(); 
  })
  .then(function(dataReadFromRemote) {
    printData(memoryData + "-" + dataReadFromRemote);
  })
  .fail(function(reason) {
    console.error(reason);
  });
};
						

But this restructuring of code can be achieved by using Promises

What are promises?

Promises are an abstraction for asynchronous programming. They are like proxies for an asynchronous computation. In future, they are evaluated into a desired value or an undesirable error.

q

javascript implementation by Kris Kowal

https://github.com/kriskowal/q
http://documentup.com/kriskowal/q

You remember this?


function readWritePromises() {
  var memoryData;

  fetchFromMemoryPromise()
  .then(function(dataFromMemoryFetch) {
    memoryData = dataFromMemoryFetch;
    return fetchFromRemoteCallPromise(); 
  })
  .then(function(dataFromRemoteFetch) {
    printData(memoryData + "-" + dataFromRemoteFetch);
  })
  .fail(function(reason) {
    console.error(reason);
  });
};
						

There is one thing above that most of us will hate

the variable memoryData

How could we get rid of it?

By combining promises


function readWriteWithSpread() {
  fetchFromMemoryPromise()
  .then(function(data) {
    return [data, fetchFromRemoteCallPromise()];
  })
  .spread(function(memoryData, remoteData) {
    printData(memoryData + "-" + remoteData);
  })
  .fail(function(reason) {
    console.log(reason);
  });
};
						

Though it is asynchronous, but this is still sequential

remote call won't happen until memory read finishes

Can we improve further?

By sending them in parallel


function readWriteParallel() {
  var memoryPromise = fetchFromMemoryPromise();
  var remotePromise = fetchFromRemoteCallPromise();

  Q.all([memoryPromise, remotePromise])
  .spread(function(memoryData, remoteData) {
    printData(memoryData + "-" + remoteData);
  })
  .fail(function(reason) {
    console.log(reason);
  });
};
						

Well, it's actually not in parallel

But one promise does not have to wait for other promise to be fulfilled

For God Sake, show me how to create promises!


function fetchFromMemoryPromise() {
  var deferred = Q.defer();
  
  setTimeout(function() {
    if(isError()){
      deferred.reject("Memory fetch failed");	
    }else{
      deferred.resolve('This is data from memory');
    }
  }, 3000);
  
  return deferred.promise;
};						

Create a deferred object

Resolve the deferred on success condition

Reject the deferred on failure condition

Create a promise from deferred and return the promise

Advantages?

  • More natural flow of control structure
  • They are truly asynchronous
  • Can easily be chained
  • Values are automatically pushed down
  • Errors are automatically bubbled up
  • Further code abstractions can be created

Questions?