JavaScript Awaits

K - Jul 2 '17 - - Dev Community

With ES2017 JavaScript got a feature called async-functions. They are a handy feature to streamline your asynchronous code a bit more.

"But Kay, I just learned that promises are the way to go! Has all my monadic struggling been in vain?!"

You are lucky, because async functions are basically syntactic sugar for promises.

Why?

Well, as with all the syntactic sugar, they clean up your code. They hide a bit of complexity, but you have to see yourself if it's worth it.

For example, a promise chain could look like this:

function f() {
  getServerData()
  .then(parseData)
  .then(filterData)
  .then(relayData)
  .catch(handleError);
}
Enter fullscreen mode Exit fullscreen mode

Writing it with an async-function, it can look like this:

async function f() {
  try {
    const dataString = await getServerData();
    const parsedData = await parseData(dataString);
    const filteredData = await filterData(parsedData);
    await relayData(filteredData);
  }
  catch(e) {
    handleError(e);
  }
}
Enter fullscreen mode Exit fullscreen mode

"Kay, are you insane?! You said it would clean up my code no problem, but look how bad you f*cked it up!"

Yes, you're right, especially if you come from a functional programming background, this must seem like utter madness. This probably wasn't the best example, but it shows one thing: Error handling works like many developers are used to, just try-catch and done. This is because async-functions enable the mix of synchronous and asynchronous code.

Another thing here is, that the awaited functions are simply returning their values now, so you don't have to mess around with the promises anymore, you can simply write your asynchronous code as if it were synchronous. This enables you to use it in other synchronous constructs, like loops or if statements.

async function f() {
  if (await isLoggedIn()) g()
  else h()
}

async function i() {
  const parsedElements = []
  while(let x = await getNextElement()) {
    let y
    try {
      y = await parse(x);
    }
    catch(e) {
      y = handleParseError(e);
    }
    parsedElements.push(y)
  }
  return parsedElements;
}
Enter fullscreen mode Exit fullscreen mode

So synchronous and asynchronous code now plays nicely together in one function and since it's just promises, you can use it with promise based functions out of the box.

function addOne(x) {
  return Promise.resolve(x + 1);
}

async function g() {
  const two = await addOne(1);
}
Enter fullscreen mode Exit fullscreen mode

This also goes the other way, if you got an async-function, you can use it as a promise based function, which it really is, somewhere else. So if you wrote all your code with async-functions and somebody else wants to use it, they aren't forced to use this feature.

async function f() {
  let data
  try {
    data = await parseData(await getData());
  }
  catch(e) {
    data = handleError(e);
  }
  return data
}

function g() {
  f().then(handleNewData);
}
Enter fullscreen mode Exit fullscreen mode

How?

To use this feature you either need

Currently the await keyword is only available inside async-functions, so you can't use it at the global scope of your JavaScript files, you always have to define a function as async.

This is a bit of a restriction, but as I said, async-functions are simply regular functions that happen to return a promise instead of their real value. So you can use them with any framework or library that either expect you to give it a promise or a promise returning function or doesn't do anything with the returned value anyway, which is the case with many functions that want simple callbacks.

const fs = require("fs");
fs.readFile('README.md', async function (e, data) {
  if (e) return console.error(e);
  if (await validateOnServer(data)) console.log("File OK");
})
Enter fullscreen mode Exit fullscreen mode

Conclusion

I think async-functions are a nice way to integrate sync and async code if you prefer imperative programming. If you already understood promises you should feel right at home with it.

For functional programming it may be a step back to hide promises, but functional programmers probably left promises behind for observables years ago.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .