DEV Community

Stephen Charles Weiss
Stephen Charles Weiss

Posted on • Originally published at stephencharlesweiss.com on

16 2

Converting Synchronous Code to Asynchronous

I often find myself looking up patterns for converting synchronous code into async variants in Javascript. Whether it’s remembering how exactly Promise chains work or what I need to do to create a Promise - there always seems to be one part that trips me up.

I wanted to document a simple, but I think representative, example of how to take a synchronous function and convert it to be asynchronous in Javascript.

I’ll be using a node function that is reading a file from the file system.

The original function is:

const findAssetSync = (name) => {
  const assetPath = path.join(__dirname, 'assets', name)
  return fs.readFileSync(assetPath, {encoding: 'utf-8'}).toString()
}

The first step is to make this function return a promise instead.

const findAssetAsync = (name) => {
    const assetPath = path.join(__dirname, 'assets', name)
    return new Promise((resolve, reject) => {
        fs.readFile(assetPath, {encoding: 'utf-8'}, (err, data) => {
            if (err) reject(err);
            return resolve(data);
          })
      })
}

Now, let’s look at how this would actually be used. I’ll start with the synchronous version.1

const server = http.createServer((req, res) => {
  const route = url.parse(req.url).pathname
  if (routes[route]) {
    const assets = findAssetSync(routes[route])
    res.write(assets)
    res.end()
  } else {
    res.writeHead(404, Not Found)
    res.end()
  }
})

To use the asynchronous version, however, we either need to convert the callback within createServer into an Async/Await function or now use a promise chain.

The point, however, is that now, instead of returning the string itself as we do in findAssetSync, findAssetAsync returns a promise.

Using Promise Chain

Promise chains create some complexity. Because we want to to make sure we resolve before moving onto the writing of the server response - we can’t do this:

const server = http.createServer(req, res) => {
  /* ... */
  if (routes[route]) {
    let assets = findAssetAsync(routes[route]).then(results => {
      assets = results;
    }).catch(err => console.error(err))
    res.write(assets)
    res.end()
  } else {
    /* ... */
  }
})

This would error, because while the promise is resolving, node would just continue moving along and reading the file — so we’d write assets (which would be undefined at the time) and then end the response.

To handle this - we place the response inside the .then block:

const server = http.createServer(req, res) => {
  /* ... */
  if (routes[route]) {
    findAssetAsync(routes[route]).then(results => {
      res.write(results)
      res.end()
    }).catch(err => console.error(err))
  } else {
    /* ... */
  }
})

It’s easy to see how, if this were to get much more complicated - and/or you wanted to carry variables forward (instead of just using the “response” variable from a Promise) how this can quickly get messy.2

Using Async/Await

The async await syntax is much simpler to reason through. While it’s not creating synchronous code - it reads as if it is. And underneath, it’s all just using Promises.

const server = http.createServer( async (req, res) => {
  /* ... */
  if (routes[route]) {
    const assets = await findAssetAsync(routes[route])
    res.write(assets)
    /* ... */
  } else {
    /* ... */
  }
})

That’s it. We’re now waiting for the Async function to resolve before preceeding - all while not blocking other requests.

Conclusion

Converting from synchronous to asynchronous javascript code is not particularly difficult. It’s a matter of understanding what is actually happening with the event loop and then pattern recognition.

Footnotes

  • 1 I have simplified the code here a bit. For example, we’re missing the routes object that I’m looking for the route.
  • 2 I found Danny Moerkeke’s article on Async Programming in JavaScript particularly helpful in highlighting this challenge.

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more

Top comments (0)

SurveyJS custom survey software

Simplify data collection in your JS app with a fully integrated form management platform. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more. Integrates with any backend system, giving you full control over your data and no user limits.

Learn more