Photo by Andreas Rønningen _ Unsplash

Native JavaScript promises – part3 : Error Handling & Chaining

Error Handling:

Error handling can be done via the catch() method or as the second parameter of the then() method. Consider these equivalent chunks of code:

//Option 1  
getJSON('../data/file.json') //error happening here
.then(function(data){
  updateView(data);
  return get(data.thisUrl); //or error happening here
})
.catch(function(error){ //

 

In some cases, then() can be interpreted as a catch():

getJSON('../data/file.json')
//There is no resolve function here, only a rejection function
.then(undefined, rejectFunction)
//In the eventuality that the promise resolves, 
//the following then() that has a resolve function will be used
.then(resolveFunction);

 

In all cases, as soon as a promise rejects, the JavaScript engine skips to the next reject function in the chain:

//Option 1  
getJSON('../data/file.json') //error happening here
.then(function(data){
  updateView(data);
  return get(data.thisUrl); //or error happening here
})
.catch(function(error){ //

 

Important note on error handling:

It is strongly recommended to use catch() instead of then() when trying to handle errors for two reasons:

  1. Code readability: catch() are easy to spot in the code, using then() for both resolving and rejecting can be confusing
  2. Execution order: The resolve and rejection functions will never both be called if they are part of the same then() while they both can be called if the rejection function is part of a catch()

 

Performing asynchronous actions:  series vs parallel

Most asynchronous operations are not isolated, one request can lead to many others. In the case of multiple asynchronous actions, they all get performed in a chain (one promise generating another promise). The performance will then differ depending on wether actions are performed in series or in parallel.

/**
 * Chaining promises:
 * Two file requests must be performed because one resource leads to the other.
 * We assume: 
 * createThumbnail(data) receives an object, generates a thumbnail and place it on stage
 * get(url) : returns a promises (produced from fetch() of the fetch API)
 * getJSON(url) : performs an XHR for a JSON and returns a promise that passes the parsed JSON response
*/
//On page load: 
  getJSON('../data/file1.json')//Make the 1st request
  //If 1st request successful, return a promise of the 2nd request
  .then(function(resource){  
    return getJSON( resource.results[0]);
  })
  //Handling errors on the 1st request
  .catch(function(){
    throw Error('Search Request Error');
  })
  //If 2nd request is successful, create thumbnail
  .then(createThumbnail)
  //Handling errors on the 2nd request
  .catch(function(error){
    console.log('error = ', error); 
  });
//On page load: 

 

Actions in series:

Actions in series occur one after another, which means that the next resource is also fetched after the current one resolves.

/**
 * Situation where two file requests must be performed in order to 
 * get the information necessary to display a thumbnail on stage.
*/
getJSON('../data/file.json')//1st request
.then(function(response) {  
  //generate a promise object that will be reused to generate a sequential request
  let sequence  = Promise.resolve();
  //For each url in the array ...
  //chain a new promise that will be resolved only after the preceeding one 
  response.results.forEach(function(url){
    sequence.then(function(){
      return getJSON(url);
    })
    .then(createPlanetThumb)
    .catch(function(error){
      throw Error('Search Request Error = ', error);
    });
  }); 
});
//https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve

 

Actions in parallel:

Actions in parallel all occur simultaneously, which means that resources are fetched and probably resolve at the same time.

Actions in parallel using Array.prototype.map():

Promises can be resolved in parallel using the map() method of the Array object like in the following example (resolve order is not guaranteed):

/**
 * We use the map() on an array of url resources and generate 
 * a promise for each url resource of the array
 * (We assume errors are handled)
*/
getJSON('../data/file.json')//1st request
.then(function(resourse){  
  resourse.results.map(function(url){ 
    getJSON(url).then(createPlanetThumb);
  });
});

 

Actions in parallel using Promise.all():

Promises can also be resolved in parallel using Promise.all() method which will simultaneously resolve all promises provided in an iterative argument (or reject them all only one of them rejects).

/**
 * We use the map() on an array of url resources and generate 
 * a promise for each url resource of the array
 * createThumbnail(data) : received an object, generates a thumbnail and place it on stage
 * getJSON(url) : performs an XHR for a JSON and returns a promise that passes the parsed JSON 
*/
getJSON('../data/file.json')//request the array of urls
.then(function(response) { 
  //returns a promise that resolves all promises promises generated in the array
  return Promise.all(response.results.map(getJSON)); 
})
.then(function(arrayOfResources){
  //create a thumbnail for each object in the array
  arrayOfResources.forEach(function(res){
    createPlanetThumb(res);
  });
})
.catch(function(error){
  console.log(error);
});

 

Conclusion:

Today, we looked into error handling, promises chaining and we finished by studying two ways of  performing multiple asynchronous operations (series and parallel).

This concludes our three parts studies on Native JavaScript Promises. Thank you.

 

Published by

Eric Njanga

Web front-end developer passionate about finding the most efficient workflow for creating the most effective user-interfaces. His dream is to become a front-end instructor. wlayouts@gmail.com / https://ca.linkedin.com/in/ericanjanga