ECMAScript: State of the Asynchronism

A synchronous operation blocks a process till the operation completes. An asynchronous operation is non-blocking and only initiates the…

ECMAScript: State of the Asynchronism
A synchronous operation blocks a process till the operation completes. An asynchronous operation is non-blocking and only initiates the operation. Completion could be discovered by some other methods.

To understand the underlying workings of asynchronism in Javascript, let's talk first about how promises uncover this completion method.

Introduced in ES(2015).

A Promise you say?

Exactly what it means, in simple words the Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.var promise1 = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('foo');
}, 300);
});promise1.then(function(value) {
console.log(value);
// expected output: "foo"
});console.log(promise1);
// expected output: [object Promise]

Explained in more detail from the docs

A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason. This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation completed successfully.
  • rejected: meaning that the operation failed.

A pending promise can either be fulfilled with a value or rejected with a reason (error). When either of these options happens, the associated handlers queued up by a promise’s thenmethod are called. (If the promise has already been fulfilled or rejected when a corresponding handler is attached, the handler will be called, so there is no race condition between an asynchronous operation completing and its handlers being attached.)

As the Promise.prototype.then() and Promise.prototype.catch() methods return promises, they can be chained

Promise flow chart.

Async / Await

Introduced in ES2017, the Async/Await function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result.

But the syntax and structure of your code using async functions are much more like using standard synchronous functions.function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}async function asyncCall() {
console.log('calling');
var result = await resolveAfter2Seconds();
console.log(result);
// expected output: 'resolved'
}asyncCall();

Explained in more detail from the docs

An async function can contain an await expression that pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the asyncfunction's execution and returns the resolved value.

Remember, the await keyword is only valid inside async functions. If you use it outside of an async function's body, you will get a SyntaxError.

Try / Catch

The try...catch statement marks a block of statements to try, and specifies a response, should an exception be thrown.

function getProcessedData(url) { 
  return downloadData(url) // returns a promise 
    .catch(e => { 
      return downloadFallbackData(url)  // returns a promise 
    }) 
    .then(v => { 
      return processDataInWorker(v); // returns a promise 
    }); 
}

This can be rewritten as

async function getProcessedData(url) { 
  let v; 
  try { 
    v = await downloadData(url);  
  } catch(e) { 
    v = await downloadFallbackData(url); 
  } 
  return processDataInWorker(v); 
}
Footnote: ES(2019) introduced the optional catch binding, which enables to simply omit the variable including the parenthesis after catch

Explained in more detail from the docs

The try statement consists of a try block, which contains one or more statements. {} must always be used, even for single statements. At least one catch clause, or a finally clause, must be present. This gives us three forms for the try statement:

  1. try...catch
  2. try...finally
  3. try...catch...finally

A catch clause contains statements that specify what to do if an exception is thrown in the try block. That is, you want the try block to succeed, and if it does not succeed, you want to pass control to the catch block. If any statement within the try block (or in a function called from within the try block) throws an exception, control is immediately shifted to the catchclause. If no exception is thrown in the try block, the catch clause is skipped.

The finally clause executes after the try block and catch clause(s) execute but before the statements following the try statement. It always executes, regardless of whether an exception was thrown or caught.

You can nest one or more try statements. If an inner try statement does not have a catchclause, the enclosing try statement's catch clause is entered.

In conclusion

Handling asynchronous code can be a pain sometimes, but if you really want to master this important skill, practicing and understanding these key concepts will help you achieve that.