Join the AI Workshop to learn more about AI and how it can be applied to web development. Next cohort February 1st, 2026
The AI-first Web Development BOOTCAMP cohort starts February 24th, 2026. 10 weeks of intensive training and hands-on projects.
If you have code that uses callbacks, you can convert it to use async/await for cleaner, more readable code.
Here’s a callback-based example:
const uploadFile = (callback) => {
//upload the file, then call the callback with the location of the file
callback(location)
}
uploadFile((location) => {
// go on
})
See? We call uploadFile and when it finishes doing what it needs to do, it calls the callback function.
To convert this to async/await, wrap the body of the function in a return new Promise() call, and when you get the data you want to return, call resolve():
const uploadFile = () => {
return new Promise((resolve, reject) => {
//upload the file, then call resolve with the location of the file
resolve(location)
})
}
const location = await uploadFile()
Now we can use the location data directly, instead of it being wrapped in the callback function.
It helps keep the code cleaner and reason better about it.
Here’s a full example. The original callback-based code:
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
}
})
})
Here’s what it looks like after converting to 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`)
})
The code is now more linear and easier to follow. Note how we use reject() for errors and resolve() for successful completion.
Lessons in this unit:
| 0: | Introduction |
| 1: | Callbacks |
| 2: | Timers |
| 3: | Promises |
| 4: | Async functions |
| 5: | Chaining promises |
| 6: | Orchestrating promises |
| 7: | Top-level await |
| 8: | Async with array methods |
| 9: | ▶︎ Converting callbacks to async/await |