Understanding Callbacks in Node.js
Master callback patterns in Node.js. Understand error-first callbacks and avoid callback hell.
Understanding Callbacks in Node.js
Callbacks are functions passed as arguments. Node.js uses them everywhere.
What's a Callback?
```javascript function greet(name, callback) { const message = `Hello, ${name}!`; callback(message); }
greet('Alice', (msg) => { console.log(msg); // Hello, Alice! }); ```
Why Callbacks?
Node.js is non-blocking. Instead of waiting: ```javascript // Blocking (bad in Node.js) const data = readFileSync('file.txt'); // Waits here console.log(data);
// Non-blocking (good) readFile('file.txt', (err, data) => { console.log(data); // Runs when file is read }); console.log('This runs immediately!'); ```
Error-First Callbacks
Node.js convention: first argument is always error.
```javascript const fs = require('fs');
fs.readFile('file.txt', 'utf8', (err, data) => { if (err) { console.error('Error:', err.message); return; } console.log('Data:', data); }); ```
Creating Error-First Callbacks
```javascript function divide(a, b, callback) { if (b === 0) { callback(new Error('Cannot divide by zero')); return; } callback(null, a / b); }
divide(10, 2, (err, result) => { if (err) { console.error(err.message); return; } console.log('Result:', result); // 5 }); ```
Callback Hell (The Problem)
```javascript // Nested callbacks = hard to read fs.readFile('file1.txt', (err, data1) => { if (err) return handleError(err); fs.readFile('file2.txt', (err, data2) => { if (err) return handleError(err); fs.readFile('file3.txt', (err, data3) => { if (err) return handleError(err); // Finally do something with data1, data2, data3 console.log(data1, data2, data3); }); }); }); ```
Avoiding Callback Hell
**1. Named functions:** ```javascript function handleFile1(err, data1) { if (err) return handleError(err); fs.readFile('file2.txt', handleFile2); }
function handleFile2(err, data2) { if (err) return handleError(err); console.log('Done!'); }
fs.readFile('file1.txt', handleFile1); ```
**2. Use Promises (better):** ```javascript const fs = require('fs').promises;
async function readFiles() { const data1 = await fs.readFile('file1.txt'); const data2 = await fs.readFile('file2.txt'); const data3 = await fs.readFile('file3.txt'); return [data1, data2, data3]; } ```
Convert Callback to Promise
```javascript const { promisify } = require('util');
// Old callback style const readFile = promisify(fs.readFile);
// Now use with async/await const data = await readFile('file.txt', 'utf8'); ```
Key Takeaway
Callbacks are foundational in Node.js. Always handle errors first. Avoid deep nesting with named functions or better yet, use Promises and async/await for cleaner code.