In this blog post, I will share a technique for converting code that uses callbacks into code that utilizes async/await. I will provide an example and explain the steps involved in this transformation.

Let’s start with the original code that uses a callback:

const uploadFile = (callback) => {
  // Upload the file, then call the callback with the location of the file
  callback(location);
}

uploadFile((location) => {
  // Continue with the logic
});

To convert this code to use async/await, we can wrap the body of the uploadFile function in a return new Promise() call. Once we have the desired data to return, we can use resolve() to indicate a successful completion:

const uploadFile = () => {
  return new Promise((resolve, reject) => {
    // Upload the file, then call the callback with the location of the file
    resolve(location);
  });
}

const location = await uploadFile();

By using this approach, we can access the location data directly in the first-level code, instead of having it wrapped within a callback function. This improves code cleanliness and makes it easier to reason about.

To illustrate this concept in a larger example, here’s the full code of an actual function:

const uploadFile = (fileName, id, callback) => {
  const fileContent = fs.readFileSync(fileName);

  const params = {
    Bucket: process.env.AWS_BUCKET_NAME,
    Key: `file.jpg`,
    Body: fileContent
  };

  s3.upload(params, (err, data) => {
    if (err) {
      throw err;
    }
    callback(data.Location);
  });
}

uploadFile(files.logo.path, job.id, async (location) => {
  await prisma.job.update({
    where: { id: job.id },
    data: {
      logo: location
    }
  });
});

And here’s how we can convert it using async/await:

const uploadFile = (fileName, id) => {
  return new Promise((resolve, reject) => {
    const fileContent = fs.readFileSync(fileName);

    const params = {
      Bucket: process.env.AWS_BUCKET_NAME,
      Key: `job-${id}.jpg`,
      Body: fileContent
    };

    s3.upload(params, (err, data) => {
      if (err) {
        reject(err);
      }
      resolve(data.Location);
    });
  });
}

handler.post(async (req, res) => {
  const files = req.files;
  const body = req.body;

  const job = await prisma.job.create({
    data: {
      ...body,
      created_at: new Date().toISOString()
    }
  });

  const location = await uploadFile(files.logo.path, job.id);

  await prisma.job.update({
    where: { id: job.id },
    data: {
      logo: location
    }
  });

  res.redirect(`/jobs/${job.id}/payment`);
});

By converting the code that uses a callback into code that takes advantage of the async/await pattern, we can improve code readability and maintainability. Now you can integrate this technique into your own projects.

Tags: callback, async/await, JavaScript, technical, code transformation