Categories
Modern JavaScript

Best Features of ES2017 —Async Functions Pitfalls

Spread the love

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the best features of ES2017.

Properties of Async Functions

Async functions don’t wrap promises that we return.

For instance, if we have:

async function asyncFunc() {
  return Promise.resolve('foo');
}

Then if we have:

asyncFunc()
  .then(x => console.log(x))

Then x is 'foo' .

If we return rejected promise, then the thrown error value will be in the catch callback.

For instance, we can write:

async function asyncFunc() {
  return Promise.reject(new Error('error'));
}

Then if we write:

asyncFunc()
  .catch(err => console.error(err));

and error is the Error instance.

Async Function Tips

We should never forget await if our code on the right side of the = is a promise.

For example, we shouldn’t write:

async function asyncFunc() {
  const value = promiseFunc();
  //...
}

That won’t work since there’s no await to wait for the result of the promise.

await makes sense even if the function doesn’t return anything.

We can use the promise a signal to pause the function until something is done.

For instance, we can write:

async function asyncFunc() {
  await sleep(2000);
  //...
}

to make the function pause for 2 seconds with our own sleep function.

We can implement sleep however we like to as long as it returns a promise.

We Don’t Always Need await

If we just want to invoke a promise and don’t want to wait for its result, then we don’t need to add await to it.

For instance, we can write:

async function asyncFunc() {
  const writer = openFile('foo.txt');
  writer.writeLine('foo');
  writer.writeLine('bar');
  await writer.close();
}

We didn’t add await to the writer.writeLine method calls because we don’t want to wait for the result.

This way, we can make our function run faster since we don’t have to wait.

await is sequential, Promise.all() is parallel

We’ve to remember that await is run sequentially.

And Promise.all runs the array of promise we pass in in paralle.

So if we have:

async function foo() {
  const result1 = await promise1();
  const result2 = await promise2();
}

then await waits for each async function is fulfilled until the next line is run.

On the other hand, if we have:

async function foo() {
  const [result1, result2] = await Promise.all([
    promise1(),
    promise2(),
  ]);
}

then promise1 and promise2 are run in parallel.

Async Functions and Callbacks

await only affects the surrounding async function.

So an async function can’t await in a callback.

However, callbacks can be async functions.

This makes callbacks tricky to use.

For instance, we can’t use await in a map callback that isn’t async:

async function download(urls) {
  return urls.map(url => {
    const content = await makeRequest(url);
    return content;
  });
}

We don’t have an async keyword in front of the function.

But we also can’t write:

async function download(urls) {
  return urls.map(async url => {
    const content = await makeRequest(url);
    return content;
  });
}

since we won’t be able to get the results of each async function from the callback.

The awaiting is only done inside the callback.

What we should do is to map our URLs to promises and then call Promise.all on the array of promises.

For instance, we write:

async function download(urls) {
  const promiseArray = urls.map(async (url) => {
    const content = await makeRequest(url);
    return content;
  });
  return await Promise.all(promiseArray);
}

We call map to map the array of URLs to an array of promises, and then we return our Promise.all call to return a promise that resolves to the results of all the promises in an array.

Conclusion

Async functions are handy to use, but it can be misused if we aren’t careful.

So we should make sure to use them properly to avoid unexpected results.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

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