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! 🚀
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).
Creates a Buffer of size 5 with zeros
Creates a Buffer of size 5 with random data
Throws an error
Creates an empty Buffer
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.
A
65
[Object object]
Undefined
Binary data
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');
}
Prints file content then "Done"
Prints "Done" then file content
Only prints file content
Throws an error
What does fs.readFileSync() return by default?
import fs from 'fs';
const content = fs.readFileSync('test.txt');
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()
Returns a Buffer
Returns a string
Returns undefined
Returns a Promise
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
- ‘close’: When the stream and underlying resource have closed
const readable = fs.createReadStream('file.txt');
readable.on('data', chunk => console.log(chunk));
readable.on('end', () => console.log('Done!'));
'data', 'end', 'error', 'close'
'finish', 'drain', 'pipe'
'read', 'write', 'end'
'open', 'close', 'data'
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().
Copies file with compression
Copies file without memory buffering
Loads entire file into memory
Creates a symbolic link
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.
Creates nested directories if needed
Creates only the last directory
Throws an error
Creates symbolic links
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.
hello world
HELLO WORLD
Error
undefined
How many times is fs.watch() guaranteed to 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() is not guaranteed to fire exactly once per logical file change. It often fires multiple times because many text editors:
- Save to a temporary file
- Rename it to the target file
For more reliable watching, consider using:
- The
chokidarpackage - Debouncing the callback
- Using
fs.watchFile()(though it’s less efficient)
Once per file change
Not guaranteed; may fire multiple times
Never
Only on file deletion
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
true
false
undefined
Error
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!
Prevents memory overflow
Increases read speed
Compresses data
Encrypts data
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');
Creates a hard link
Creates a copy
Creates a symbolic link
Moves the file
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 });
}
});
Binary mode only
Object mode only
Both modes
Neither mode
Input & output modes
Read & write modes
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;
});
A number
A string
An object
A buffer
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>
3
4
6
8
10
I hope you enjoyed testing your NodeJS IO knowledge! Want more? Check out my Quiz Collection for more challenges!
Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.