Skip to content

Instantly share code, notes, and snippets.

@JudahSan
Last active October 26, 2023 11:44
Show Gist options
  • Save JudahSan/8a63a0b1a9c031d9ae3f03d9a38950b7 to your computer and use it in GitHub Desktop.
Save JudahSan/8a63a0b1a9c031d9ae3f03d9a38950b7 to your computer and use it in GitHub Desktop.
Node Fundamentals

Asynchronous JavaScript

  • JS is a synchronous, blocking, single-threaded language

  • JS is blocking - no matter how long a previous process takes, the subsequent processes won't kick off until the former is completed

  • Single threaded- a thead is a process that your JS program can use to run a task. Each thread can only do one task at a time. JS has just the one thread called the main thread for executing any code.

  • We need new pices outside JS to help us write asynchronous code.

  • For FE, this where web browsers come into play. For BE, this is where Node.js comes into play

  • Web browsers and Node.js define functions and APIs that allow us to register functions that should not be executed synchronously, and should instead be invoked asynchronously when some kind of event occurs.

  • Eg, that could be the passage of time (setTimeout or setInterval), the user's interaction with the mouse (addEventListener),, data being read from a file system or the arrival of data over the network (callbacks, Promises, async-await)

const buffer = new Buffer.from("Kamagera");
// Write to the buffer
// Buffers have limited memory
// This overwrites the previous characters
buffer.write("Code");
// outputs an option of type and data(hexadecimal representaion of the string)
console.log(buffer.toJSON());
// outputs the binary rep of the string(in decimal form)
console.log(buffer);
// Outputs the string
console.log(buffer.toString())

Higher-Order Functions:

A higher-order function is a function that takes one or more functions as arguments or returns a function as its result. In JavaScript, functions are first-class citizens, which means they can be treated like any other data type such as strings, numbers, or objects. Higher-order functions leverage this feature to enable more concise and expressive code.

Example:

function higherOrderFunction(callback) {
  // Do something before calling the callback
  console.log("Preparing to call the callback function...");
  
  // Call the callback function
  callback();
}

function callbackFunction() {
  console.log("Inside the callback function!");
}

// Passing the callback function to the higher-order function
higherOrderFunction(callbackFunction);

In this example, higherOrderFunction is a higher-order function because it takes callbackFunction as an argument and then calls it within its body.

Callback Functions:

A callback function is a function that is passed as an argument to another function and is executed after some operation has been completed. Callback functions are commonly used in asynchronous programming, where a function needs to wait for an operation to complete (such as reading a file or making an API request) before it can continue executing.

Example:

// Asynchronous function that takes a callback
function fetchData(callback) {
  setTimeout(function() {
    const data = "Data from the server";
    callback(data);
  }, 2000); // Simulating a delay of 2 seconds
}

// Callback function passed to fetchData
function displayData(data) {
  console.log("Data received: " + data);
}

// Calling the asynchronous function with the callback
fetchData(displayData);

In this example, fetchData is an asynchronous function that takes displayData as a callback. The fetchData function simulates fetching data from a server and calls the displayData callback with the fetched data after the operation is complete.

Callbacks are essential in scenarios where you want to perform actions after certain tasks are finished, especially when those tasks take a significant amount of time to complete, like fetching data from a server or reading a large file. Callbacks allow you to maintain the flow of your program without getting blocked by time-consuming operations.

Synchronous Callbacks

A callback which is executed immediaterly. eg sort, filter, map

Asynchronous callbacks

  • A callback that is often used to continue or resume code execution after an asynchronous operation has completed
  • Callbacks are used to delay the execution of a function until a particular time or event has occured
  • In Node.js most modules have an asynchronous nature to prevent blocking of execution eg network calls, fetching data from a db
// Functions are first class objects
// A function can be passed as an argument to a function
// A functin can also be returned as values from other functions
const greet = name => console.log(`Hello ${name}`);
const higherOrderFunction = callback => {
const name = "Uzumaki";
callback(name);
}
higherOrderFunction(greet);
// Asynchronous callbacks in browser: event hadnlers
callback = () => document.getElementById("demo").innerHTML = "Hello World";
document.getElementById("btn").addEventListener("clickk", callback);
// Data fetching JQuery
$.get("url", function (data) {
$(".result").html(data);
alert("Load was performed.");
});

Character Sets

  • Character sets are predefined list of characters represented by numbers
    • Popular charater sets - Unicode and ASCII
"V"..charCodeAt() // 86

Character Encoding

  • Character encoding dictates how to represent a number in a character set as binary data before it can be stored in a computer
  • It dictates how many bits to use to represent the number
  • eg UTF-8 - characters should be encoded in bytes (8bits)
  • 4 => 100 -> 00000100
const EventEmitter = require("node:events");
const emitter = new EventEmitter();
// Response. Reg emiiter
emitter.on('order-pizza', (size, topping)=>{
console.log(`Order received~ Baking a ${size} pizza with ${topping}`);
})
// Register multiple listeners
emiter.on('order-pizza', (size) => {
if (size === 'large') {
console.log('Serving complimentary drink');
// Broadcast event Add data to the listener
emitter.emit("order-pizza", "large", "mushroom");

Events Scenario

Events allow us to right code in a none blocking manner

  • Ordering pizza -> the event
  • Bake pizza -> response to that event
const EventEmitter = require("node:events");

const emitter = new EventEmitter();

// Response. Reg emiiter
emitter.on('order-pizza', (size, topping)=>{
  console.log(`Order received~ Baking a ${size} pizza with ${topping}`);
})

// Register multiple listeners
emiter.on('order-pizza', (size) => {
  if (size === 'large') {
    console.log('Serving complimentary drink');


// Broadcast event Add data to the listener
emitter.emit("order-pizza", "large", "mushroom");

pizza-shop.js

const EventEmitter = require("node:events");

class PizzaShop extends EventEmitter {
  constructor() {
    super();
    this.orderNumber = 0;
  }
  order() {
    this.orderNumber++;
    this.emit("order", size, topping);
}
  displayOrderNumber() {
  console.log(`Current order number: ${this.orderNumber}`);
  }
}

modules.exports = PizzaShop;

index.js

const PizzaShop = require("./pizza-shop");
const DrinkMachine = require("./drink-machine");

const pizzaShop = new PizzaShop();
const drinkMachine = new DrinkMachine();

pizzaShop.on("order", (size, topping)=>{
  console.log(`Order received! Baking a ${size} pizza with ${topping}`);
  drinkMachine.serverDrink(size);
});

pizzaShop.order("large", "mushroom");
pizzaShop.displayOrderNumber();

drink-machine.js

class DrinkMachine {
  serveDring(size) {
    if (size === "large") {
      console.log("Serving complimentary drink");
    }
  }
}

modules.exports = DrinkMachine;

index.js fs-promise-module

const fs = require("node:fs/promises");

fs.readFile("file.txt", "utf-8")
.then((data) => console.log(data)) // Called whenn promise resolves successfully
.catch((error) => console.log(error)) // Called when promise rejects with an error

// better alternative
async function readFile() {
  try {
    const data = await fs.readFile("file.txt", "utf-8")
    console.log(data)
  } catch(err) {
    console.log(err)
  }
}
readFile();

index.js old method

const fs = require("node:fs")

file fileContents = fs.readFileSynch("./file.txt")  
console.log(fileContents) // <Buffer ...>

file fileContents = fs.readFileSynch("./file.txt", "utf-8")  
console.log(fileContents) // Kenothia

fs.readFile("./file.txt", "utf-8" (error, data) =>{
    if (error) {
    console.log(error)
    } else {
    console.log(data)
    }
})

// Write files to file
fs.writeFileSync("./greet.txt", "Ola mi amigo")

fs.writeFile("./greet.txt", "Hello Motto", { flag: "a" }, (err) => {
  if(err) {
    console.log(err)
  } else {
    console.log("File written")
  } 
})

file.txt

Kenothia

Path Module

// Import the path module in Node.js
const path = require("node:path");

// Print the base filename of the current module
console.log(path.basename(__filename)); // Output: "filename.js"

// Print the last portion of the directory path of the current module
console.log(path.basename(__dirname)); // Output: "current_directory"

// Print the file extension of the current module
console.log(path.extname(__filename)); // Output: ".js"

// Print an empty string because directories don't have file extensions
console.log(path.extname(__dirname)); // Output: ""

// Parse the given file path into an object containing its parts
console.log(path.parse(__filename)); 
// Output: { root: '/',
//           dir: '/path/to/current_directory',
//           base: 'filename.js',
//           ext: '.js',
//           name: 'filename' }

// Format the parsed object back into a file path
console.log(path.format(path.parse(__filename))); // Output: "/path/to/current_directory/filename.js"

// Check if the given path is an absolute path and print true or false accordingly
console.log(path.isAbsolute(__filename)); // Output: true

// Check if the given path is an absolute path and print false because it's a relative path
console.log(path.isAbsolute("./data.json")); // Output: false

// Join the given path segments into a single path using the platform-specific delimiter (/ or \)
console.log(path.join("folder1", "folder2", "index.html")); // Output: "folder1/folder2/index.html"

// Join the given absolute path segments into a single path
console.log(path.join("/folder1", "folder2", "index.html")); // Output: "/folder1/folder2/index.html"

// Resolve redundant slashes in the path and join the segments into a single path
console.log(path.join("/folder1", "//folder2", "index.html")); // Output: "/folder1/folder2/index.html"

// Go up one level in the directory hierarchy and join the segments into a single path
console.log(path.join("/folder1", "//folder2", "../index.html")); // Output: "/folder1/index.html"

// Join the current module's directory path with "data.json"
console.log(path.join(__dirname, "data.json")); // Output: "/path/to/current_directory/data.json"

// Resolve the given path segments into an absolute path
console.log(path.resolve("folder1", "folder2", "index.html")); // Output: "/path/to/current_directory/folder1/folder2/index.html"

// Resolve the given absolute path segments into an absolute path
console.log(path.resolve("/folder1", "folder2", "index.html")); // Output: "/folder1/folder2/index.html"

// Resolve redundant slashes in the path and then resolve it to an absolute path
console.log(path.resolve("/folder1", "//folder2", "index.html")); // Output: "/folder1/folder2/index.html"

// Resolve the path and go up one level in the directory hierarchy
console.log(path.resolve("/folder1", "//folder2", "../index.html")); // Output: "/folder1/index.html"

// Resolve the current module's directory path and join it with "data.json"
console.log(path.resolve(__dirname, "data.json")); // Output: "/path/to/current_directory/data.json"

A promise is a proxy for a value not necessarily know when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason.

What?

  • A promise is simply an object in JS
  • A promise is always in one of the 3 states
    • pending: which is initial state, neither fulfilled nor rejected
    • fulfilled: meaning that the operation completed successfully
    • rejected: meaning that the operaton failed

Why?

Promises help us deal with asynchronous code in a far more simpler way compared to callbacks

How to work with Promises?

  1. Promise

  2. Promise value

  3. Fulfill promise

  4. Reject promise

  5. Success callback

  6. Failure callback

  7. How to create a Promise?

  8. How to fulfill or reject the Promise?

  9. How to execute callback functions based on whether the Promise is fulfilled or rejected?

  • Promise status: pending to fulfilled? then() is executed
  • Promise status: pending to rejected? catch() is executed
const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    // Change the status from 'pending' to fulfilled
  resolve('Bringing item')
  }, 5000)
  setTimeout(() => {
    // Change status from pending to rejected
  reject('Not bringing items')
  }, 5000)
  
}}
promise.then(onFulfillment)
promise.catch(onRejection)

// Success and failure callbacks
const onFulfillment = () => {
  // resolve was called
  console.log(result)
  console.log('Set up the table to eat tacos')
}

const onRejection = () => {
  // reject was called
  console.log(error)
  console.log('Start cooking pasta')
}

Streams

  • A stream is a sequence of data that is being moved from one point to another over time eg movement of data over the internet from one computer to another
  • In Node js the idea is to Process streams of data in chuncks as they arrive instead of waiting for the entire data to be available before preocessing eg watching video on YouTube

Buffers

  • An intentionally small area that Node maintains in the runtime to process a stream of data. insert bus stop analogy
  • eg streaming a video online
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment