Skip to content

Node.js

Exercise 1 : Callbacks, Event Loop & Emitter

1. Chaining Callbacks

Write a Node.js program that defines a function add(a, b, callback) which adds two numbers and returns the result via a callback. Chain this with another callback to multiply the result by 10 and log it.

js
// add function with a callback
function add(a, b, callback) { 
  const sum = a + b;
  callback(sum);
}

// First callback calls the second (chaining)
add(10, 20, (val) => {
  const result = val * 10;
  console.log(`Result after multiplication: ${result}`);
});
js
// Named callback function for the add operation
function handleSum(sum) {
  const result = sum * 10;
  console.log(`Result after multiplication: ${result}`);
}

function add(a, b, callback) {
  const sum = a + b;
  callback(sum);
}
// Calling with a named callback
add(10, 20, handleSum);

2. Demonstrating Asynchronous Behavior

Create a countdown timer using setTimeout(). Use setTimeout() and console.log() to demonstrate asynchronous behavior, and add another setTimeout with 1000ms to execute.

js
// Part 1: Countdown Timer

console.log("Countdown starts now!");
setTimeout( () => { console.log("3"); }, 1000);
setTimeout(() => { console.log("2"); }, 2000);
setTimeout(() => { console.log("1"); }, 3000);
setTimeout(() => { console.log("Go!"); }, 4000);

// Part 2: Demonstrating the Event Loop
console.log("\n--- Demonstrating Async ---");
console.log("Start");

setTimeout(() => {
  console.log("Async Task A (executes after 1000ms)");
}, 1000);

setTimeout(() => {
  console.log("Async Task B (executes after 500ms)");
}, 500);

console.log("End");
Expected Output Order for Async Demo:
Start
End
Async Task B (executes after 500ms)
Async Task A (executes after 1000ms)

3. Using the Event Emitter

Create an event emitter that emits a greet event and logs a message. Then, emit a login event with a username and log "<username> has logged in".

js
const EventEmitter = require('events');
const emitter = new EventEmitter();

// Register a listener for the 'greet' event
emitter.on('greet', () => {
  console.log('Hello! Welcome to Node.js Events.');
});

// Register a listener for the 'login' event
emitter.on('login', (username) => {
  console.log(`${username} has logged in.`);
});

// Emit the events
emitter.emit('greet');
emitter.emit('login', 'John');
  • emitter.on() to register a listener (subscribe to an event).

  • emitter.emit() to trigger the event, passing data along with it.

Exercise 2 – Node.js: Buffers, Streams & File System

1. Reading a File

Use fs.readFile() to read and display the contents of a file named info.txt.

Create a file named info.txt with some content in the same Folder

js
const fs = require('fs');

// Reads and displays the content of info.txt asynchronously
fs.readFile('info.txt', 'utf8', (err, data) => {
  if (err) {
    console.error("Error reading file:", err);
    return;
  }
  console.log("\nContents of info.txt:");
  console.log(data);
});
js
const fs = require('fs');

// Named callback function for reading the file
function handleFileData(err, data) {
  if (err) {
    console.error("Error reading file:", err);
    return;
  }
  console.log("\nContents of info.txt:");
  console.log(data);
}
// Reads and displays the content of info.txt asynchronously
fs.readFile('info.txt', 'utf8', handleFileData);

2. Writing and Reading Files

Write a program to write "Welcome to Node.js" into a file named welcome.txt. Then, read the content of welcome.txt and log it using a callback.

js
const fs = require('fs');

// Write to the file
fs.writeFile('welcome.txt', 'Welcome to Node.js', (err) => {
  if (err) {
    return console.error('Error writing to file:', err);
  }
  console.log('File written successfully!');

  // Read the file after writing is complete
  fs.readFile('welcome.txt', 'utf8', (err, data) => {
    if (err) {
      return console.error('Error reading the file:', err);
    }
    console.log('File content:', data);
  });
});

3. Using Readable Streams

Use a readable stream to read data.txt and log chunks to the console. Explain the benefit of using streams over fs.readFile().

Create a file named data.txt with some text.

js
const fs = require('fs');

const readStream = fs.createReadStream('data.txt', 'utf8');

readStream.on('data', (chunk) => {
	console.log('--- New Chunk Received ---');
	console.log(chunk);
});

readStream.on('end', () => {
	console.log('\n--- Finished Reading File ---');
});

readStream.on('error', (err) => {
	console.error('An error occurred:', err);
});

Benefit of Streams:

  • fs.readFile(): Loads the entire file into RAM at once. This is simple but can cause a crash if the file is larger than the available memory.

  • fs.createReadStream(): Reads the file in small, manageable chunks. This is highly memory-efficient and allows you to process enormous files without issue. It's like sipping water through a straw instead of trying to drink the whole lake at once.


4. Working with Buffers

Create a buffer from the string "Node.js" and print it in hexadecimal form. Then, modify the first letter of the buffer from "N" to "C" and print the result.

js
// Create a buffer from a string
const buffer = Buffer.from('Node.js');

// Print the buffer in hexadecimal form
console.log('Hexadecimal form:', buffer.toString('hex'));

// Modify the buffer's first byte ('N' -> 'C')
buffer[0] = 'C'.charCodeAt(0);

// Print the modified buffer as a string
console.log('Modified Buffer:', buffer.toString());

Exercise 3 – Git

1. Initializing a Repository and Making the First Commit

You are starting a personal website project. Initialize a Git repository, create an index.html file, check the status, stage the file, commit it, and view the history.

bash
# Create and navigate into the project directory
mkdir my-website
cd my-website

# Initialize a new Git repository
git init

# Create a basic index.html file
echo "<h1>Welcome to My Website!</h1>" > index.html

# Check the status (shows index.html as an untracked file)
git status

# Stage the new file for the next commit
git add index.html

# Commit the staged file with a descriptive message
git commit -m "Initial commit: Add index.html"

# View the commit history
git log

2. Branching and Merging

You want to build a new feature. Create a new branch feature-navbar, add a navbar.html file, commit the change, and then merge it back into the main branch.

bash
# Create a new branch and switch to it
git checkout -b feature-navbar

# Create a simple navbar.html file
echo "<nav>Home | About | Contact</nav>" > navbar.html

# Stage the new navbar file
git add navbar.html

# Commit the change on the feature branch
git commit -m "feat: Add navigation bar"

# Switch back to the main branch
git checkout main

# Merge the feature-navbar branch into main
git merge feature-navbar

# (Optional) View the log to see the merge commit
git log --oneline --graph

Made with ❤️ for students, by a fellow learner.