Graham King

Solvitas perambulum

Javascript’s async/await and Promise in a few words

Summary
In JavaScript, the common pattern for handling asynchronous functions is to use callbacks, as seen in the `myFunc(value, successCallback, errorCallback)` format. To improve this, JavaScript introduced the `Promise` object, allowing us to write `myFunc(value).then(successCallback, errorCallback)`, which makes it easier to chain operations without passing callbacks. The introduction of `async` and `await` further simplifies asynchronous code, enabling us to write it in a linear fashion, such as `await myFunc(value); successCallback();`, while still handling errors with try-catch. This eliminates callback hell and makes the code much cleaner and easier to read, as long as we remember to handle exceptions.

This is a very common pattern in Javascript:

myFunc(value, successCallback, errorCallback)

If myFunc could block (such as on network access), we don’t want to block Javascript’s single thread, so we asked to be notified when the operation is complete.

Javascript added syntactic sugar to make this pattern more pleasant, in two steps.

The first step is adding a Promise object. myFunc would now returns a Promise, and the caller looks like this:

myFunc(value).then(successCallback, errorCallback)

Very similar. An improvement is you can now return this Promise up the call chain, instead of passing down your callbacks, and you can chain them. I sometimes think of Promise as ‘CallbackManager’. It wraps them and gives them a nicer interface.

The second step, and the key one, is the two new keywords async and await, that allow you to do this:

async function wrapper() {
  await myFunc(value);
  successCallback();
}

If a function returns a Promise, we can put await in front and pretend that in the success case our code is linear. It reads a lot better. The failure case throws an exception:

async function wrapper() {
  try {
    await myFunc(value);
    successCallback();
  } catch (e) {
    errorCallback();
  }
}

The async keyword, as far as I can tell, is just a marker for the Javascript interpreter to watch out for some voodoo within.

The function just looks linear. It executes like a callback, in that other code will run while myFunc blocks.

Let’s un-sugar an example, working backwards:

async function wrapper() {
  // code before
  try {
    await myFunc(value);
    // code after
  } catch (e) {
    // err case which you always
    // handle because you are a professional
  }
}

Is equivalent to:

function wrapper() {
  // code before
  myFunc(value).then(
    function(){ // code after },
    function(){ // err case }
  );
}

which is the modern way of writing:

function wrapper() {
  // code before
  myfunc(value,
    function(){ // code after },
    function(){ // err case }
  );
}

await offers a straightforward solution to Javascript’s callback-hell by making code significantly easier to read. Just remember to catch the exception.