0% found this document useful (0 votes)
11 views10 pages

Understanding Promises in Node.js

The document discusses the use of Promises and Async/Await in JavaScript for handling asynchronous operations, including reading and writing files. It explains how to chain Promises and handle errors using try/catch blocks. Additionally, it covers different types of errors in Node.js and the importance of error handling in programming.

Uploaded by

Rehan Hussain
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views10 pages

Understanding Promises in Node.js

The document discusses the use of Promises and Async/Await in JavaScript for handling asynchronous operations, including reading and writing files. It explains how to chain Promises and handle errors using try/catch blocks. Additionally, it covers different types of errors in Node.js and the importance of error handling in programming.

Uploaded by

Rehan Hussain
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

const fs = require('fs')

let readFilePromise = file => {


return new Promise((resolve, reject) => {
[Link](file, 'UTF-8', (err, data) => {
if(err) {
reject(err)
} else {
resolve(data)
}
})
})
}

readFilePromise('[Link]')
.then(d => [Link](d))
.catch(e => [Link](e))

Promises are usuefull especially when combining multiple callbacks such as when
making an HTTP server and serving files inside which will be covered in the later chapters.

Chained promises
When working with multiple asynchronous operations, we can chain the promises.
Returning anything in the then block will make you able to chain another then block. Here is
how:

let multiply = (x, y) => {


return new Promise((resolve, reject) => {
resolve(x * y)
})
}

multiply(9, 8)
.then(v => {
[Link](v)
return v * 5
})
.then(v => {
[Link](v)
})

As you can see, I’ve returned a number in my first, then that makes it possible to receive in the
second then block.

136
A then block can return a new promise object as well:

let multiply = (x, y) => {


return new Promise((resolve, reject) => {
resolve(x * y)
})
}

multiply(4, 4)
.then(v => {
[Link](v)
return new Promise((resolve, reject) => resolve(v ** 2))
})
.then(v => {
[Link](v)
})

The result of the code above will be:

16

256

If a then block returns a promise that rejects, the error will be received by the catch block
chained to the main chain:

multiply(4, 4)
.then(v => {
[Link](v)
return new Promise((resolve, reject) => reject("Error"))
})
.then(v => {
[Link](v)
})
.catch(e => [Link](e))

Meaning that the catch block is shared between the main promise and the promise returned from
the first then block. Alternatively, you can return the promise itself in a then block:

let cube = n => {


return new Promise((resolve, reject) => {
resolve(n ** 3)
})
}

137
cube(2)
.then(v => {
[Link](v)
return cube(v)
})
.then(v => {
[Link](v)
return cube(v)
})
.then(v => {
[Link](v)
return cube(v)
})

The output of this code is as below:

16

134217728

Async/Await
As you can imagine using multiple promises inside each other can be a real mess. An
async function can permit you to use the await keyword. The await keyword allows you to
simplify the usage of a promise and wait till it resolves. An async function is defined using the
async keyword as below:

async function myFirstAsyncFunction() {


//
}

Here is a sample of a promise being handled inside an async function:

const fs = require('fs')

let readFilePromise = file => {


return new Promise((resolve, reject) => {
[Link](file, 'UTF-8', (err, data) => {
if(err) reject(err)
else resolve(data)
})
})

138
}

async function myFunc(file) {


let data = await readFilePromise(file)
[Link](data)
}

myFunc('[Link]')

As you see, there is no then block, and we only used our promise with an await keyword
prepended. In this situation, if our promise rejects, we can handle it using a try/catch block.
(You’ll read about this in the next chapter)

async function myFunc(file) {


try {
let data = await readFilePromise(file)
[Link](data)
} catch(e) {
[Link](e)
}
}

myFunc('[Link]')

It is useful to use an async function when combining multiple promises. Here we will read a file
and write its content to another one using two promises and an async function:

const fs = require('fs')

let readFilePromise = file => {


return new Promise((resolve, reject) => {
[Link](file, 'UTF-8', (err, data) => {
if(err) reject(err)
else resolve(data)
})
})
}

let writeToFilePromise = (file, data) => {


return new Promise((resolve, reject) => {
[Link](file, data, (err) => {
if(err) reject(err)
else resolve('Done')
})

139
})
}

async function copy(fileToRead, fileToWrite) {


try {
let data = await readFilePromise(fileToRead)
await writeToFilePromise(fileToWrite, data)
} catch(e) {
[Link](e)
}
}

copy('[Link]', '[Link]')

This way it is cleaner and readable to use multiple promises and manage their resolves and
rejects, but it is still your choice to use either way.

140
Chapter 11: Errors and
Error Handling
In every program by any programmer, there might be several errors, and there is no
shame in it but not understanding them or not handling them is shameful. [Link] is no exception
and can experience four categories of errors:

1- Standard JavaScript errors


a. <EvalError>
b. <SyntaxError>
c. <RangeError>
d. <ReferenceError>
e. <TypeError>
f. <URIError>
2- System errors
3- User-specified errors
4- AssertionError

All JavaScript errors and System errors raised by [Link] inherit from or are instances of, the
standard JavaScript <Error> class.

The type of error can be determined from the error log. The code below throws an error
of type ReferenceError.

let a = 5
[Link](b)

And is seen in the log:

141
Each error usually has some description of what is happening in front of the error type:

As well it shows you where the error is happening:

“C:\Book samples\[Link]:13” indicates that there is an error in a file located at C:\Book


samples\[Link] on line 2, column 13.

It is really important and crucial to know how to read an error since it will help you move
forward faster if there are any errors.

Throw and Try/Catch


Errors are usually thrown, which should be caught. If not, [Link] will exit the program
immediately. Throwing an error is done by the throw keyword, and for handling it, there is a
statement called try/catch. Throwing errors is usually done by the programmer specifically or by
most of the synchronous APIs (You will read about synchronous and asynchronous APIs later).

An error can be thrown like below:

throw 'This is my error!'

142
And the output of the program would be as this:

throw "This is my error!"

This is my error!

(Use `node --trace-uncaught ...` to show where the exception was thrown)

Any code after a thrown error in case it is not handled won’t be executed since the program gets
quit as soon as an error is thrown. So the code below will only print the first line, and then after
throwing an error, there will be no results.

[Link]('First line')
throw 'I\'m an error!'
[Link]('Second line')

By using the try/catch statement, we can prevent such incidents from happening. Any error
thrown inside a try block will immediately be handled by a catch block receiving the error.

try {
throw 'Kaboom!'
} catch(error) {
[Link](error)
}

Now that an error is thrown inside a try/catch, it won’t bother the rest of the program anymore,
and if there are any codes after it, they can be executed normally.

Having the code below:

[Link]('First log')

try {
throw 'Kaboom!'
} catch(error) {
[Link](error)
}

[Link]('Second log')

The result in the command line would be as below:

First log
143
Kaboom!

Second log

It might seem obsolete to throw and catch an error like this, and yes, it is! But sometimes it is not
you who throws an error. It can be some other API, some other function, or anything else. For
instance, when reading a file using the file system (will be discussed more later), if a file is not
present, it can throw an error:

const fs = require('fs')

[Link]('./non_existing_file.txt')

This code generates the output below:

internal/fs/[Link]

throw err;

Error: ENOENT: no such file or directory, open './non_existing_file.txt'

at [Link] ([Link]:3)

at [Link] ([Link]:35)

at Object.<anonymous> (C:\Book samples\[Link]:4)

at Module._compile (internal/modules/cjs/[Link]:30)

at [Link]._extensions..js (internal/modules/cjs/[Link]:10)

at [Link] (internal/modules/cjs/[Link]:32)

at [Link]._load (internal/modules/cjs/[Link]:14)

at [Link] [as runMain] (internal/modules/run_main.js:74:12)

at internal/main/run_main_module.js:18:47 {

errno: -4058,

144
syscall: 'open',

code: 'ENOENT',

path: './non_existing_file.txt'

Which stops the program as we’ve seen in the previous example. But it can be handled so there
won’t be any problem:

const fs = require('fs')

try {
[Link]('./non_existing_file.txt')
} catch(e) {
[Link](e)
}

Caution: A try/catch statement has its own scope.

Error class
JavaScript has a generic Error class that defines an error regardless of the type, and other
error types extend this class. The constructor of this class receives a message.

throw new Error('I am an error!')

This code generates the error logged below:

throw new Error('I am an error!')

Error: I am an error!

at Object.<anonymous> (C:\Book samples\[Link]:7)

at Module._compile (internal/modules/cjs/[Link]:30)

at [Link]._extensions..js (internal/modules/cjs/[Link]:10)

at [Link] (internal/modules/cjs/[Link]:32)
145

You might also like