Last active
March 31, 2021 04:17
-
-
Save abdulloooh/b91e9019162eeddbadb00290f8054a72 to your computer and use it in GitHub Desktop.
Advanced Node.js lessons (LinkedIn Learning)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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")) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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