Site Loader
async-await-callback-promise

Share with your friends!

Hi all, After a long time of silence I thought of continuing writing again. During the past months, I was working on improving my knowledge in several areas and contributing to community activities such as ng-Sri Lanka. So I think now it is the time to share my learnings and experiences with all of you.

You may already know the context I am going to share if you are a web platform dev. yeah, I am going to share some thoughts and experiences as well as a comparison on async-await, promises and callback. JavaScript is a strange language. Here we are trying to build up a deterministic behaviour of the code flow. or else we will end up with lots of issues with missing data and unexpected behaviours of the event loop.

event loop is a programming construct or design pattern that waits for and dispatches events or messages in a program.
the event loop circular execution. (Image is taken from the internet)

In Javascript, It runs a single-threaded event loop and in the first go around it will run all of the synchronous code and it queues up asynchronous events such as get some data from the network to be called back later.

The event loop runs continuously through synchronous codes and during the time, the separate thread pool will execute the asynchronous codes. Then the event loops will be notified that it is ready to be called back now sometime in the future to continue the previously skipped executions.

There can be two types of tasks.

  1. Macro Tasks
  2. Micro Tasks

Macro Tasks

The macro tasks queue executes in the next event loop. The below methods are examples of these tasks.

– setTimeout(function, milliseconds)
– setInterval(function, milliseconds)

Micro Tasks

The microtask queue called back before the start of the next event loop. The fulfilled promise is an example of these tasks.

Let’s try some code to get this code execution flow.

Promise.resolve().then(() => console.log('Promise'));<br>
console.log('Synchronous code')
setTimeout(() => console.log('Timeout'));<br>
console.log('Sychronous code-2');

Callbacks

There isn’t a special thing called a ‘callback’ in the JavaScript language, it’s just a convention. Functions that use callbacks take some time to produce a result instead of immediately returning some result like most functions. The requirement of callbacks occurs when doing I/O, e.g. downloading things, reading files, talking to databases, etc.

downloadPhoto('http://coolcats.com/cat.gif', handlePhoto)
function handlePhoto (error, photo) {
  if (error) console.error('Download error!', error)
  else console.log('Download finished', photo)
}
console.log('Download started')

In the above code, handlePhoto is the callback function to the downloadPhoto function. This callback function will execute after the downloading completes. In simple scenarios like this, callbacks are easy to understand and easy to write as well. But let’s think about a scenario like we have several actions to take sequentially. Then the code will end up like this in callback hell.

const prepareOrangeJuice = nextStep => {
  getOranges(function (oranges) {
    sliceOranges(oranges, function (slicedOranges) {
      removeSeeds(sliceOranges, function (seedRemovedOrangeSlices) {
        juiceOrange(seedRemovedOrangeSlices, function(freshOrangeJuice, water, sugar) {
          prepareDrink(freshOrangeJuice, sugar, water, function(orangeJuice) {
          	nextStep(orangeJuice)
          })
        })
      })
    })
  })
}

// Make and serve the orangeJuice
prepareOrangeJuice(function (orangeJuice) => {
  serve(orangeJuice)
})

Here we have to use several callbacks for each action. Code is getting larger and larger as a chunk introducing callback hell. There was a solution for that as keeping callback functions separately. It worked but still not the better approach since the code can introduce callback hell.

Developers had some struggle when facing this problem. The code was hard to read and follow. Then, the promise introduced as a solution.

Promises

A promise is an object that will return a value in future. These are well suits for asynchronous JavaScript operations due to the “in future” behaviour. This can make callback hell much easier to manage. Instead of the nested code, you can have a nicely aligned code which is easy to read and understand.

A promise can be,

fulfilled – The action relating to the promise succeeded
rejected – The action relating to the promise failed
pending – Hasn’t fulfilled or rejected yet
settled – Has fulfilled or rejected

getSomethingWithPromise()
  .then(data => {/* do something with data */})
  .catch(err => {/* handle the error */})

The promise behaves like verbal language. Simply, “then block” executes successful behaviour and catch executes the failure behaviour. This failure can occur due to rejection of promise or error executing then block. There is another way of writing a promise is as follows.

getSomethingWithPromise().then(function(response) {
 	/* do something with data */
}, function(error) {
  	/* handle the error */
})

In this way, promise won’t handle the errors occurred during the success path. Because of that, the best practice of writing a promise is ending with .catch(). Let’s see what will happen if the promise chain gets larger.

function promiseHell() {
	let bookId;
  	let authorId;
  	let reviews;
  
  	dataSource.getUser().then(u => {
    	return dataSource.getAuthor(u.id).then(x => x.json())
    })
  	.then(a => {
    	authorId = a.id;
      	return dataSource.getBook(authorId).then(x => x.json())
    })
  	.then(b => {
    	bookId = b.id;
      	return dataSource.getReviews(bookId).then(x => x.json())
    })
  	.then(r => {
    	reviews = r;
   	})
  	.catch(err => {
      console.log(err);
    });
}

Promises are a huge improvement for callbacks. The code is getting complex, then it makes really hard to read and follow especially when we have a long chain of asynchronous multiple events even though we have used Promises. Finally, async-await introduced in order to fix this.

Async Await

Asynchronous functions make it easier to write asynchronous JavaScript. There are a new coding pattern needs for this. We can easily organize the code more manageable way with async-await.

async function functionName (arguments) {
  // Do something asynchronous
}

The basic structure would be like this for async function. It is required to use the async keyword to specify that this function handles some asynchronous actions and make the use of await keyword is also available within the async code block.

Asynchronous functions always return promises. We can handle this promise easily rather than handling a chain of asynchronous multiple events.

const test = async _ => {
  const one = await getOne(false)
  const two = await getTwo(false)
  const three = await getThree(false)
}

test()
  .catch(error => console.log(error)))

If we try to write the same book-author code using await it would be like this.

async function handlewithAsyncAwait() {
  	let reviews;
  
  	try {
      const user = await dataSource.getUser();

      const author = await dataSource.getAuthor(user.id);
      const authorData = await author.json();

      const book = await dataSource.getBook(authorData.id);
      const bookData = await book.json();

      const reviews = await dataSource.getReviews(bookData.id);
      const reviewsData = await reviews.json();
    } catch(err) {
    	console.log(err);
    }
}

You can decide whether you need to use try-catch or catch on return promise based on the design of the code. Both work the same. If you need to have a specific error handling mechanism you also can use try-catch for each await like this.

const test = async _ => {
  try {
    const one = await getOne(false)
  } catch (error) {
    console.log(error) // Failure!
  }

  try {
    const two = await getTwo(false)
  } catch (error) {
    console.log(error) // Failure!
  }

  try {
    const three = await getThree(false)
  } catch (error) {
    console.log(error) // Failure!
  }
}

test();

Next up, we’re going to look at advanced uses of promises, multiple awaits with error handling and combine promises and async functions to get better and improved code.

Thanks for reading. Did this article help you out? If it did, I hope you consider sharing it. You might help someone else out. Thanks so much!

Share with your friends!

2 Replies to “The async-await that I love as promised to the callback”

Leave a Reply

Your email address will not be published. Required fields are marked *