Quiz: NodeJS IO Mastery
Test your knowledge of files, streams & buffers
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! 🚀
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)
.
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.
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');
}
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:
- Specify an encoding:
fs.readFileSync('test.txt', 'utf8')
- Convert the Buffer:
content.toString()
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!'));
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()
.
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
.
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:
- Converted to a string
- Transformed to uppercase
- Passed to stdout
This creates a pipeline that converts all input to uppercase.
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:
- Save to a temporary file
- 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)
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
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!
File System
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');
Streams
What modes can Node.js streams operate in?
Streams can operate in:
- Binary mode (default): for buffers and strings
- 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 });
}
});
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;
});
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>
I hope you enjoyed testing your NodeJS IO knowledge! Want more? Check out my Quiz Collection for more challenges!