Categories
JavaScript Best Practices

Better JavaScript — Async Code

Spread the love

Like any kind of apps, JavaScript apps also have to be written well.

Otherwise, we run into all kinds of issues later on.

In this article, we’ll look at ways to improve our JavaScript code.

Error Handling with Async Code

Like synchronous code, async code errors also need to be handled.

Handling async code errors may be trickier than synchronous code.

Synchronous code errors can be caught with a try-catch block:

try {
  f();
  g();
  h();
} catch (e) {
  //...
}

We catch the errors in the catch block.

And e has whatever is throw in the code in the try block.

Async code comes in a few forms.

If it’s a callback, then some may send the error with the callback.

For instance, Node style callbacks send the error:

fs.readFile('/foo.txt', (err, data) => {
  if (err) {
    throw err;
  }
  console.log(data);
});

In the fs.readFile method, err has the error that’s set when there is one.

We can check for the err value and do something.

Promise errors come in the form of rejected promises.

A promise can be rejected with an error object.

For instance, we can write:

Promise.reject(new Error('error'))

Then we can call the catch method to catch it:

Promise.reject(new Error('error'))
  .catch(err => console.error(err));

The callback’s err parameter has the error.

The async and await looks more like synchronous code.

We can use try-catch to catch rejected promises.

For instance, we can write:

async function foo() {
  try {
    const val1 = await f;
    const val2 = await g;
    const val3 = await h;
  } catch (err) {
    console.error(err);
  }
}

We just wrap our code with try and catch errors with catch like synchronous code.

Async Loops

Async code can be run sequentially in a loop.

The for-await-of loop lets us run async code sequentially.

For instance, we can write:

async function foo() {
  for await (const p of promises) {
    const val = await p;
    console.log(val);
  }
}

We have the for-await-of loop inside the async function which runs the promises in sequence.

It can work with any async iterable object.

Async Callback and Recursion

Async callbacks don’t have any way to run them sequentially easily.

The only way we can do it is with recursion.

For instance, we can write:

function downloadOneAsync(urls, onsuccess, onfailure) {
  const n = urls.length;

  function download(i) {
    if (i >= n) {
      onfailure("error");
      return;
    }
    downloadAsync(urls[i], onsuccess, () => {
      download(i + 1);
    });
  }
  download(0);
}

We have the download function that runs recursively.

The success callback runs the download function once the previous call is successful.

It runs until i reaches n and then stops.

Don’t Block Event Queue on Computations

We shouldn’t block the event queue on complex computations.

If we have some really long-running, then we can create a worker to run it.

Then the task will run in the background.

For instance, we can create a Worker instance by writing:

const myWorker = new Worker('worker.js');

Then we can send messages to it by writing:

myWorker.postMessage(value);

We can listen to messages from the worker by writing:

myWorker.onmessage = function(e) {
  const result = e.data;
  //...
}

Then in worker.js , which is the worker, we can get the message by writing:

onconnect = function(e) {
  const port = e.ports[0];

  port.onmessage = function(e) {
    const data = e.data;
    //...
    port.postMessage(workerResult);
  }
}

The port gets the connection to the main thread.

And e.data gets the data from the main thread sents with myWorker.postMessage .

We can send messages with the postMessage method.

Conclusion

We can loop through promises sequentially.

Also, we can create web workers to run long-running tasks in the background.

The only way to run a series of async callbacks sequentially is with recursion.

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 *