This post is going to show, by way of code examples, how to take a callback based API, modify it to use Promises and then use the Async/Await syntax. This post won't go into a detailed explanation of callbacks, promises or the Async/Await syntax. For such a detailed explanation of these concepts please check Asynchronous JavaScript, which is a section of MDN Web Docs, that explains asynchronicity and how callbacks, promises, and the Async/Await syntax helps with working with asynchronous JavaScript.
This post is meant for the developer who has somewhat of an understanding of asynchronicity in JavaScript, but requires a straight to the point code example to serve as a quick syntax reference for how to take a callback based API, update it to use promises and finally use the Async/Await with it.
For demonstration purposes, we are going to use the fs.readFile, which is a callback based API from reading files. We will have a file test.txt
that would contain some text, we will then have a file script.js
that would open the file, read the contents, and print it to the terminal.
The code will first be implemented using callbacks, it would then be updated to use Promises, and finally, instead of using Promise directly, it will be updated to use Async/Await.
Let's get started.
Using Callbacks
We first create a directory where we are going to work from, create also the file that will contain our code, and the two files that we would be reading from.
We first create the two files with contents.
$ mkdir ~/code
$ touch ~/code/script.js
$ echo "Beam me up, Scotty" > ~/code/test.txt
$ cd ~/code/
Next in the script.js
file, we have the following code:
const fs = require("fs")
function readFileCallBack() {
fs.readFile("./test.txt", 'utf8', (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data.trim() + " [callback]")
})
}
readFileCallBack()
Executing the script by running node script.js
should get "Beam me up, Scotty" printed to the terminal:
$ node script.js
Beam me up, Scotty [callback]
Using Promises
Update script.js
and add a version of readFileCallback
that uses promises. It looks like this:
function readFilePromise() {
return new Promise((resolve, reject) => {
fs.readFile("./test.txt", 'utf8', (err, data) => {
if (err) {
reject(err)
return
}
resolve(data.trim())
})
});
}
readFilePromise()
.then(data => console.log(data + " [promise]"))
.catch(err => console.log(err))
Execute the script by running node script.js
:
$ node script.js
Beam me up, Scotty [callback]
Beam me up, Scotty [promise]
Using Async/Await
Update script.js
and add a third version that uses the Async/Await syntax. Since Async/Await is a syntax that makes using promises easier, the Async/Await implementation would make use of the readFilePromise()
function. It looks like this:
async function readFileAsync() {
try {
const data = await readFilePromise()
console.log(data.trim() + " [async-await]")
} catch (err) {
console.log(err)
}
}
readFileAsync()
Executing the script by running node script.js
will print something similar to this, to the terminal:
Beam me up, Scotty [callback]
Beam me up, Scotty [promise]
Beam me up, Scotty [async-await]
The complete file with the 3 implementations is presented below:
const fs = require("fs")
// callback
function readFileCallBack() {
fs.readFile("./test.txt", 'utf8', (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data.trim() + " [callback]")
})
}
readFileCallBack()
// promise
function readFilePromise() {
return new Promise((resolve, reject) => {
fs.readFile("./test.txt", 'utf8', (err, data) => {
if (err) {
reject(err)
return
}
resolve(data.trim())
})
});
}
readFilePromise()
.then(data => console.log(data + " [promise]"))
.catch(err => console.log(err))
// async/await
async function readFileAsync() {
try {
const data = await readFilePromise()
console.log(data.trim() + " [async-await]")
} catch (err) {
console.log(err)
}
}
readFileAsync()
Error Handling
To illustrate that the error handling in the 3 implementation work as expected, rename the test.txt
file and rerun the script:
$ mv test.txt test.txt.backup
$ node script.js
[Error: ENOENT: no such file or directory, open './test.txt'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './test.txt'
}
[Error: ENOENT: no such file or directory, open './test.txt'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './test.txt'
}
[Error: ENOENT: no such file or directory, open './test.txt'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: './test.txt'
}
Showing that the error handling code, which is to just print the error to the console, works as expected in the 3 implementations.
Top comments (0)