Dan Levy's Avatar DanLevy.net

Quiz: NodeJS IO Mastery

Test your knowledge of files, streams & buffers

Quiz: NodeJS IO Mastery

Ready to dive into the world of NodeJS IO? 🌊

This quiz will test your understanding of Node’s IO operations, from basic file system operations to advanced streaming concepts. We’ll cover buffers, encoding, and best practices for handling data efficiently.

Let’s see how well you know your streams from your buffers! 🚀

1. 

Warmup: Buffers

What does this code do?

const buf = Buffer.alloc(5);
console.log(buf);

Buffer.alloc(size) creates a new Buffer of specified size filled with zeros. The output will be: <Buffer 00 00 00 00 00>

If you want to create a Buffer with random data, use Buffer.allocUnsafe(5).

Learn more about Buffer allocation

2. 

Warmup: Buffers

What will this print?

const buf = Buffer.from([65]);
console.log(buf.toString());

The numbers in the array represent ASCII codes:

  • 65: ‘A’

toString() converts these bytes to their string representation using UTF-8 encoding by default.

Learn more about Buffer encoding

3. 

Warmup: File System

What’s the output order?

import fs from 'fs';
fs.readFile('test.txt', 'utf8', (err, data) => {
  console.log(data);
});
console.log('Done');

Since readFile is asynchronous, the code continues executing while the file is being read. Therefore, “Done” will be printed before the file content.

To wait for the file to be read first, you could use the Promise-based version:

import { promises as fs } from 'fs';

async function read() {
  const data = await fs.readFile('test.txt', 'utf8');
  console.log(data);
  console.log('Done');
}

4. 

File System Basics

What does this code return?

import fs from 'fs';
const content = fs.readFileSync('test.txt');
console.log(typeof content);

fs.readFileSync() returns a Buffer by default when no encoding is specified. If you want a string, you need to either:

  1. Specify an encoding: fs.readFileSync('test.txt', 'utf8')
  2. Convert the Buffer: content.toString()

Learn more about fs.readFileSync on Node.js docs

5. 

Streams

Which set of events are commonly used with Readable streams?

Readable streams emit several important events:

  • ‘data’: When data is available to be read
  • ‘end’: When there is no more data to read
  • ‘error’: When an error occurs
  • ‘finish’: When all data has been flushed to the underlying system
const readable = fs.createReadStream('file.txt');
readable.on('data', chunk => console.log(chunk));
readable.on('end', () => console.log('Done!'));

Learn more about Stream events

6. 

Streams

What does this code do?

import fs from 'fs';
const readable = fs.createReadStream('source.txt');
const writable = fs.createWriteStream('dest.txt');
readable.pipe(writable);

pipe() connects a readable stream to a writable stream, automatically managing backpressure and copying data in chunks without loading the entire file into memory.

This is memory-efficient for large files compared to fs.readFile() followed by fs.writeFile().

Learn more about pipe()

7. 

File System

What does the recursive option do?

import fs from 'fs';
fs.mkdirSync('./a/b/c', { recursive: true });

The recursive: true option creates parent directories if they don’t exist. Without this option, trying to create ‘./a/b/c’ would throw an error if ‘./a’ or ‘./a/b’ don’t exist.

This is similar to the shell command mkdir -p.

Learn more about mkdir

8. 

Streams

What will this output?

import { Transform } from 'stream';
const upperCase = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString().toUpperCase());
  }
});
process.stdin
  .pipe(upperCase)
  .pipe(process.stdout);
// Input: "hello world"

Transform streams modify data as it passes through. Here, each chunk is:

  1. Converted to a string
  2. Transformed to uppercase
  3. Passed to stdout

This creates a pipeline that converts all input to uppercase.

Learn more about Transform streams

9. 

File System

How many times will the callback fire when a file is modified?

import fs from 'fs';
fs.watch('test.txt', (eventType, filename) => {
  console.log(`${filename} was changed`);
});
// Then modify test.txt once

fs.watch() often fires twice for a single change because many text editors:

  1. Save to a temporary file
  2. Rename it to the target file

For more reliable watching, consider using:

  • The chokidar package
  • Debouncing the callback
  • Using fs.watchFile() (though it’s less efficient)

Learn more about fs.watch()

10. 

Buffers

What’s the output?

const buf1 = Buffer.from('Hello');
const buf2 = Buffer.from('Hello');
console.log(buf1 === buf2);

Buffers are compared by reference, not value. Even though they contain the same data, they are different objects.

To compare Buffer contents, use:

buf1.equals(buf2)  // true
// or
Buffer.compare(buf1, buf2) === 0  // true

Learn more about Buffer comparison

11. 

Streams

What’s the main purpose of stream backpressure?

Backpressure is a mechanism that prevents memory overflow by pausing reading when the writing end can’t keep up.

Example of manual backpressure:

readable.on('data', (chunk) => {
  const canContinue = writable.write(chunk);
  if (!canContinue) {
    readable.pause();
    writable.once('drain', () => readable.resume());
  }
});

pipe() handles this automatically!

Learn more about backpressure

12. 

What does this code do?

import fs from 'fs';
fs.symlinkSync('target.txt', 'link.txt');

symlinkSync creates a symbolic link (like a shortcut) to the target file.

Key differences from hard links:

  • Can link to directories
  • Can span file systems
  • Breaks if target is deleted

To create a hard link instead:

fs.linkSync('target.txt', 'hardlink.txt');

Learn more about symlinks

13. 

Streams

What modes can Node.js streams operate in?

Streams can operate in:

  1. Binary mode (default): for buffers and strings
  2. Object mode: for any JavaScript value

Example of object mode:

import { Transform } from 'stream';
const objectStream = new Transform({
  objectMode: true,
  transform(chunk, encoding, callback) {
    callback(null, { value: chunk });
  }
});

Learn more about stream modes

14. 

File System

What type is the fd parameter in this callback?

import fs from 'fs';
fs.open('test.txt', 'r', (err, fd) => {
  console.log(typeof fd);
});

File descriptors are numbers that uniquely identify open files in the operating system.

The first three file descriptors are reserved:

  • 0: stdin
  • 1: stdout
  • 2: stderr

Always remember to close file descriptors:

fs.close(fd, (err) => {
  if (err) throw err;
});

Learn more about file descriptors

15. 

Encoding

How many bytes will this string take in UTF-8?

const str = "Hello 🌍";
const buf = Buffer.from(str);
console.log(buf.length);

In UTF-8:

  • ASCII characters (like ‘Hello ’) take 1 byte each
  • The earth emoji 🌍 takes 4 bytes

So: 5 (Hello) + 1 (space) + 4 (🌍) = 10 bytes

To see the bytes:

console.log(buf);  // <Buffer 48 65 6c 6c 6f 20 f0 9f 8c 8d>

Learn more about UTF-8 encoding

Quiz Score:

Congrats! Quiz completed.

I hope you enjoyed testing your NodeJS IO knowledge! Want more? Check out my Quiz Collection for more challenges!

Edit on GitHubGitHub