Asynchronous Vs Synchronous in NodeJS
Table of contents
Total diffrent
Async:
Send request
go on with other code
response come in any time on a callback Sync:
Send request
Wait for response
go on with other code after response
example of Sync
console.log('Before')console.log('After')
output:
BeforeAfter
example of Async
console.log('Before')setTimeout(()=>console.log("helllo"), 2000);console.log('After')
output:
BeforeAfterhelllo
Patterns for dealing with Async Code
what's dealing in async code? see below examples:
First example:
console.log('Before')const user = get_user();console.log(user)console.log('After')function get_user() {setTimeout(() => {console.log('connect to database')return { id: 123 }}, 2000);}
BeforeundefinedAfterconnect to database
Seconde example:
console.log('Before')const user = get_user();console.log(user)console.log('After')function get_user() {setTimeout(() => {console.log('connect to database')return { id: 123 }}, 2000);return 10}
Before10Afterconnect to database
In two above examples code not work correctly beacause of 2000 milisecond delay in setTimeout function.
1-Callback Function
callback means you can pass function as arguments. First solution is callback function see below example:
console.log('Before')getUser((user) => {getRepo(user.id, (id) => {console.log(id)})});console.log('After')function getUser(callback) {setTimeout(() => {console.log('connect to database')callback({ id: 123 })}, 2000);}function getRepo(id, callback) {setTimeout(() => {console.log("Looking for repo ...")callback(['1', '2', '3'])}, 2000)}
Callback Hell
When callback functions are too many read and maintane code is so hard and we say callback hell because ou program look like below:
//AsyncgetUser((user) => {getRepo(user.id, (id) => {getCommit(()=>{...})})});
If we programed in synchronous the program is like this
console.log('Before')const user = getUser(user)const repos = getRepo(user.id)const commits = getCommits(repos[0])console.log('After')
How can we prevent callback hell and nested structuer? We can use named function instaed of anonymuse function
const { func } = require("joi");console.log('Before')getUser(getRepo)console.log('After')function getRepo(id) {getRepo(user.id, getCommits)}function getCommits(repos) {getCommits(repos, displayCommits);}function displayCommits(commits) {console.log(commits)}function getUser(callback) {setTimeout(() => {console.log('connect to database')callback({ id: 123 })}, 2000);}function getRepo(id, callback) {setTimeout(() => {console.log("Looking for repo ...")callback(['1', '2', '3'])}, 2000)}function getCommits(repos, callback) {setTimeout(() => {console.log('connect to database')callback(repos)}, 2000)}
Promise
What is promise? It's object that holds the eventual result of an asynchronous operation. This object has three states:
- Pending: when the promise object is created
- Fulfilled: when the async operation is complete and successfull
- Rejected: when the async operation has an error
const p = new Promise((resolve, reject) => {setTimeout(() => {resolve(12) // pending => resolved,fulfilledreject(new Error('massage')) // pending => rejected}, 2000)})p.then(result => console.log(result))// if resolve function occur.catch(err => console.log(err))// if reject function occur
12orError: massage
We can replace promises with callback functions lik this
const { Promise } = require("mongoose");console.log('Before')getUser().then(user => getRepo(user)).then(repos => getCommits(repos[0])).then(commits => console.log(commits)).catch(err => console.log(err.message))//catch any error that occurconsole.log('After')function getUser() {return new Promise((resolve, reject) => {setTimeout(() => {console.log('connect to database')resolve({ id: 123 })}, 2000);})}function getRepo(id) {return new Promise((resolve, reject) => {setTimeout(() => {console.log("Looking for repo ...")resolve(['1', '2', '3'])}, 2000)})}function getCommits(repos) {return new Promise((resolve, reject) => {setTimeout(() => {console.log('connect to database')resolve(repos)}, 2000)})}
BeforeAfterconnect to databaseLooking for repo ...connect to database1
Note
Some time we want to create promise that always is resolve(it's common for write unit test) do it like below
const p = Promise.resolve({id:23})p.then(user => console.log(user))//orconst p = Promise.reject(new Error("User Not found"))p.catch(err => console.log(err))
Parallel
We can running promises in parallel programming
// First Promiseconst p1 = new Promise((resolve) => {setTimeout(() => {console.log("Done 1 ...")resolve(1)}, 2000)});// Seconde Promiseconst p2 = new Promise((resolve) => {setTimeout(() => {console.log("Done 2 ...")resolve(2)}, 2000)});// Run all promise at same timePromise.all([p1, p2]).then(result => console.log(result))// The result is array// Run all promise at same time but return which one complete fasterPromise.race([p1, p2]).then(result => console.log(result))// The result is one item
Done 1 ...Done 2 ...[ 1, 2 ]Done 1 ...1Done 2 ...
3-Async/Await
In async function we can have await keyword for waiting for code execution, but important note is await keyword block execute of async function and doesn't block single thread of nodejs
For more information link
Async/Await is base on the promise and we use it because more readable and code gets more clear. Allways we should use await oprations in async function and for error handling use try/catch block.
async function displayCommit() {try {const user = await getUser();const repo = await getRepo(user.id);const commit = await getCommits(repo);console.log(commit)}catch (err) {console.log(err.message)}}displayCommit()function getUser() {return new Promise((resolve, reject) => {setTimeout(() => {console.log('connect to database')resolve({ id: 123 })}, 2000);})}function getRepo(id) {return new Promise((resolve, reject) => {setTimeout(() => {console.log("Looking for repo ...")resolve(['1', '2', '3'])}, 2000)})}function getCommits(repos) {return new Promise((resolve, reject) => {setTimeout(() => {console.log('Commite is found')resolve(repos)}, 2000)})}
