Skip to content

Instantly share code, notes, and snippets.

@abdulloooh
Last active March 31, 2021 04:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save abdulloooh/b91e9019162eeddbadb00290f8054a72 to your computer and use it in GitHub Desktop.
Save abdulloooh/b91e9019162eeddbadb00290f8054a72 to your computer and use it in GitHub Desktop.
Advanced Node.js lessons (LinkedIn Learning)
1 ==== INTRO
//CALLBACKS
//Sync with no callback
function hideString(str){
return str.replace(/[A-Za-z]/g, 'x')
}
const hidden = hideString("Hello world")
console.log(hidden)
console.log("end")
// Still sync but now with a callback
function hideString(str, done){
done(str.replace(/[A-Za-z]/g, 'x'))
}
hideString("Hello world", (hidden) =>{
console.log(hidden)
})
console.log("end")
// Async, wait till next tick
function hideString(str, done) {
process.nextTick(() => {
done(str.replace(/[a-zA-Z]/g, "x"));
});
}
hideString("Hello world", (hidden) => {
console.log(hidden);
});
console.log("end");
//Using proper delay & demonstrating callback hell or Pyramid of doom for sequential excetutions
function delay(seconds, done) {
setTimeout(done, seconds * 1000);
}
console.log("starting delays");
delay(2, () => {
console.log("2 seconds");
delay(1, () => {
console.log("3 seconds");
delay(1, () => {
console.log("4 seconds");
});
});
});
console.log("end");
///PROMISES
//normal delay with promise
const delay = (seconds) =>
new Promise((resolves, rejects) => {
setTimeout(resolves, seconds * 1000);
});
delay(10).then(() => console.log("2 seconds"));
console.log("end");
//promise resolving with an argument & demonstrating console.log as a fcn
const delay = (seconds) =>
new Promise((resolves, rejects) => {
setTimeout(() => {
resolves("message");
}, seconds * 1000);
});
delay(2)
.then(console.log)
console.log("end");
//Chaining of sequential execution
//EXAMPLE1
const delay = (seconds) =>
new Promise((resolves, rejects) => {
setTimeout(() => {
resolves("message");
}, seconds * 1000);
});
delay(2)
.then(console.log)
.then(() => "returned value")
.then(console.log)
.then(() => console.log("real end"));
console.log("end");
//EXAMPLE2
const delay = (seconds) =>
new Promise((resolves, rejects) => {
setTimeout(() => {
resolves("message");
}, seconds * 1000);
});
delay(2)
.then((res) => {
console.log("2 seconds", res);
return delay(2);
})
.then((res) => console.log("Another two seconds", res));
console.log("end");
//Catching error from REJECT or THROWN or eevn unexpected errors
const delay = (seconds) =>
new Promise((resolves, rejects) => {
// throw new Error("Error intentionally thrown")
if (seconds > 4) rejects(new Error(`${seconds}seconds too long`));
setTimeout(() => {
resolves("the long wait has ended");
}, seconds * 1000);
});
delay(5)
.then(console.log)
.catch((err) => console.log(err.message));
console.log("end first tick");
//CONVERTING CALLBACK TO PROMISE
//CALLBACK STRUCTURE USING PROMISIFY
const delay = (seconds, callback) =>{
if (seconds > 4) callback(new Error(`${seconds}seconds too long`));
else setTimeout(() => {
callback(`the ${seconds} seconds delay is over`);
}, seconds * 1000);
}
delay(2, console.log)
console.log("end first tick");
//promise version
import {promisify} from "util"
const delay = (seconds, callback) =>{
if (seconds > 4) callback(new Error(`${seconds}seconds too long`));
else setTimeout(() => {
callback(null, `the ${seconds} seconds delay is over`);
}, seconds * 1000);
}
const delayPromise = promisify(delay)
delayPromise(3)
.then(console.log)
.catch(console.log)
console.log("end first tick");
//CONVERTING FS METHODS THAT NORMALLY USE CALLBACK TO PROMISE
import { promisify } from 'util'
import { writeFile } from "fs";
//example of usual fs with callback
writeFile("file.txt", "Hello world", (err, res) => {
if (err) console.log("error creating file");
else console.log("file successfully created);
});
//comverting the fs method to promise
const writeFilePromise = promisify(writeFile)
writeFilePromise('sample.txt', 'hello world')
.then(()=>console.log("file successfully created"))
.catch(()=>console.log("error creating file"))
/**
* async ensures that the function returns a promise, and wraps non-promises in it.
* await makes JavaScript wait until that promise settles and returns its result BUT does not block main thread.
* promise with .then does not suspend the block execution but still handles whatever response, just like callback
* promise.all() and promise.race() for parallel execution
*/
/*
1. async function returns a promise. The converse is also true. Every function that returns a promise can be considered as async function
2. await is used for calling an async function and wait for it to resolve or reject.
3. await blocks the execution of the code within the `async function` in which it is located.
4. If the output of function2 is dependent on output of function1 then I use await.
5. If two functions can be run in parallel create two different async functions and then run them in parallel.
6. To run promises in parallel create an array of promises and then use Promise.all(promisesArray)
7. Every time you use await remember that you are writing blocking code. Over the time we tend to neglect this.
8. Instead of creating huge async functions with many await asyncFunction() in it, it is better to create smaller async functions. This way we will be aware of not writing too much of blocking code.
9. Another advantage of using smaller async functions is that you force yourself to think what are the async functions that can be run in parallel.
10. If your code contains blocking code it is better to make it an asyncfunction. By doing this you are making sure that somebody else can use your function asynchronously.
11. By making async functions out of blocking code, you are enabling the user who will call your function to decide on the level of asynhronicity he wants.
*/
// SEQUENTIAL EXECUTION
// CALLBACK (initially) hell, pyramid of doom
var fs = require("fs");
var beep = () => process.stdout.write("\x07");
const doStuffSequentially = () => {
console.log("starting");
setTimeout(() => {
console.log("waiting");
setTimeout(() => {
console.log("waiting some more");
fs.writeFile("file.txt", "Sample File...", (error) => {
if (error) {
console.error(error);
} else {
beep();
console.log("file.txt created");
setTimeout(() => {
beep();
fs.unlink("file.txt", (error) => {
if (error) {
console.error(error);
} else {
console.log("file.txt removed");
console.log("sequential execution complete");
}
});
}, 3000);
}
});
}, 2000);
}, 1000);
};
doStuffSequentially();
// Do better with Promise
const beep = () => process.stdout.write("\x07");
const { promisify } = require("util");
const fs = require("fs");
const writeFile = promisify(fs.writeFile);
const unlink = promisify(fs.unlink);
const delay = (s) =>
new Promise((resolves, rejects) => {
if (s === 2) return rejects("error");
setTimeout(resolves, s * 1000);
});
const doStuffSequentially = () =>
Promise.resolve()
.then(() => "starting")
.then(console.log)
.then(() => delay(1))
.then(() => "waiting")
.then(console.log)
.then(() => delay(3))
.then(writeFile("file.txt", "Sample File..."))
.then(() => beep())
.then(() => "file.txt created")
.then(console.log)
.then(() => delay(4))
.then(() => unlink("file.txt"))
.then(() => beep())
.then(() => "file.txt removed")
.then(console.log)
.then(() => console.log("sequential execution complete"))
.catch(console.log);
doStuffSequentially();
// and even better with async...await
//benefit of async await is you can isolate the action you want to catch its error
const beep = () => process.stdout.write("\x07");
const { promisify } = require("util");
const fs = require("fs");
const writeFile = promisify(fs.writeFile);
const unlink = promisify(fs.unlink);
const doStuffSequentially = async () => {
try {
console.log("starting");
await delay(1);
console.log("waiting");
await delay(2);
await writeFile("file.txt", "Sample File...");
beep();
console.log("file.txt created");
await delay(4);
await unlink("file.txt");
beep();
console.log("file.txt removed");
console.log("sequential execution complete");
} catch (err) {
return Promise.reject(err);
}
return Promise.resolve("done");
};
doStuffSequentially().then(console.log).catch(console.error);
// PARALLEL EXECUTION
// PARALLEL EXECUTION with promise.all and promise.race
let start = Date.now() / 1000;
Promise.all([delay(4), delay(5), delay(3)])
.then(console.log)
.then(() => {
const finish = Date.now() / 1000;
console.log("duration:", finish - start);
})
.catch(console.log);
start = Date.now() / 1000;
Promise.race([delay(4), delay(5), delay(3)])
.then(console.log)
.then(() => {
const finish = Date.now() / 1000;
console.log("duration", finish - start);
})
.catch(console.log);
/**
* For some tasks where lots of intensive async actions are necessary
* Running all tasks in parallel might take too much resources & overload cpu
* Running sequentially will take way too much time
*
* So, we need a sort of promise queue that is hybrid, groups parallel/concurrent actions sequentially => CONCURRECNY
*
* Also, uses `beep` and `log-update` of node.js
*/
const beep = () => process.stdout.write("\x07");
const logUpdate = require("log-update");
const delay = (s) => {
return new Promise((resolves) => {
setTimeout(resolves, s * 1000);
});
};
const toX = (n) => "x".repeat(n);
class PromiseQueue {
constructor(promises = [], concurrency = 1) {
this.concurrentCount = concurrency;
this.todo = promises;
this.running = [];
this.completed = [];
}
get runAnother() {
return this.todo.length && this.running.length < this.concurrentCount;
}
graphTasks() {
const { todo, running, completed } = this;
beep();
logUpdate(`
todo: [${toX(todo.length)}]
running: [${toX(running.length)}]
completed: [${toX(completed.length)}]
`);
}
run() {
while (this.runAnother) {
const promise = this.todo.shift();
promise.then(() => {
this.completed.push(this.running.shift());
this.graphTasks();
this.run();
});
this.running.push(promise);
this.graphTasks();
}
}
}
const tasks = [
delay(3),
delay(5),
delay(2),
delay(6),
delay(1),
delay(3),
delay(2),
];
const delayQueue = new PromiseQueue(tasks, 2);
delayQueue.run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment