Home

Promisify

TLDR; easily wrap your functions to leverage await/async (go to the end of this page)

Context

Node uses an event-driven model. What does that mean? In node you essentially send out commands and write listeners when the command is done.

For example, if you want to read the content of a file you do:

fs.readFile('temp.txt', 'utf8', (err,res) => {
console.log('res', res)
})

Here you send out a command to read a file called temp.txt (that is written in 'utf8') and in the callback you tell your computer what to do when the command finished. This listener accepts 2 arguments. err if something went wrong (e.g., file does not exist) or res if the command successfully completed.

What is wrong with this? You get callback hell (google it)

To illustrate callback hell, lets say you want to read file temp.txt, temp2.txt and temp3.txt in that order. To guarantee the order you need to start the command to read temp2 after you've read temp 1 and you need to start the command to read temp3 after you've read temp 2.

It would look like this

fs.readFile('temp.txt', 'utf8', (err,res) => {
fs.readFile('temp2.txt', 'utf8', (err,res2) => {
fs.readFile('temp3.txt', 'utf8', (err,res3) => {
// do stuff here
})
})
})

So how do you solve callback hell?

You use promises.

With promises, The above would look like this:

new Promise((resolve,reject) => {
fs.readFile('temp.txt','utf8', (err,res) => {
if(err) {
if(err){
reject(err)
} else {
resolve(res)
}
}
})
}).then(res => {
return new Promise((resolve,reject) => {
fs.readFile('temp2.txt','utf8', (err,res2) => {
if(err) {
if(err){
reject(err)
} else {
resolve({res, res2})
}
}
})
}).then(({res, res2}) => {
return new Promise((resolve,reject) => {
fs.readFile('temp3.txt','utf8', (err,res3) => {
if(err) {
if(err){
reject(err)
} else {
resolve({ res, res2, res3 })
}
}
})
})}).then(({res, res2, res3}) => {
// do stuff here with the results
})

This has a flat structure (i.e. no callbacks in callbacks) but is still verbose.

Ideally you want to write the code above like this

const res = readFile('temp.txt', 'uft8')
const res2 = readFile('temp2.txt', 'uft8')
const res3 = readFile('temp3.txt', 'uft8')
// do stuff with res, res2 and res3

How would you do that?

You would do it like this

const promisify = fnc => (...args) => {
return new Promise((resolve,reject) => {
fnc(...args,(err,res) => {
if(err) {
reject(err)
} else {
resolve(res)
}
})
})
}
const readFile = promisify(fs.readFile)
const res = await readFile('temp.txt', 'uft8')
const res2 = await readFile('temp2.txt', 'uft8')
const res3 = await readFile('temp3.txt', 'uft8')