Suggest an editImprove this articleRefine the answer for “What is promisification?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Promisification** converts a callback-based function into one that returns a Promise, letting you use `async/await` instead of nested callbacks. ```javascript const readFile = util.promisify(fs.readFile); const data = await readFile('file.txt', 'utf8'); ``` **Key:** `util.promisify` handles the standard Node.js `(err, value)` callback pattern automatically.Shown above the full answer for quick recall.Answer (EN)Image**Promisification** converts a callback-based function into one that returns a Promise, so you can use `.then()`, `.catch()`, or `async/await` instead of nested callbacks. ## Theory ### TL;DR - Callbacks are like passing a note to a friend: "call me when done." Promisification wraps that into a request with a clear result, either success or failure. - Main difference: replaces `(err, result) => {}` nesting with `.then(result => {}).catch(err => {})`. - Use it for legacy Node.js APIs (`fs.readFile`, `crypto`) and old third-party libraries. Skip it when the API already returns a Promise. - `util.promisify` handles the standard Node.js `(err, value)` callback pattern automatically. ### Quick example ```javascript const fs = require('fs'); const util = require('util'); // Original callback style fs.readFile('data.txt', 'utf8', (err, data) => { if (err) return console.error(err); console.log(data); // Output: file contents }); // Promisified - same result, no nesting const readFile = util.promisify(fs.readFile); const data = await readFile('data.txt', 'utf8'); console.log(data); // Output: file contents ``` Both do the same thing. The promisified version lets you `await` it and handle errors with a single `try/catch`. ### Key difference The Node.js callback pattern expects `(err, result)` as the last argument of every async call. Promisification wraps that function in a new one that creates a `Promise` internally, passes a generated callback to the original, and resolves or rejects based on what comes back. You write the wrapper once and use it everywhere. ### When to use - Node.js built-in APIs (`fs`, `crypto`, `dns`) that predate Promises: promisify them and switch to `async/await`. - Third-party libraries stuck on callback style: wrap once, chain everywhere. - Legacy code you cannot rewrite: promisify the interface, leave the internals untouched. - API already returns a Promise: skip promisification entirely. ### How it works internally `util.promisify` (added in Node.js v8.0.0) checks if the target function has a `util.promisify.custom` symbol defined. If not, it assumes the last argument is a standard `(err, value)` callback. It returns a wrapper that creates a `new Promise`, calls the original with a generated callback, and either calls `resolve(value)` or `reject(err)`. ### Common mistakes **Mistake: promisifying functions that do not follow the Node.js error-first style.** ```javascript const sleep = util.promisify(setTimeout); // TypeError: callback is not a function ``` `setTimeout` takes a plain callback, not `(err, value)`. Use `new Promise(resolve => setTimeout(resolve, 1000))` directly. **Mistake: losing `this` when promisifying class methods.** ```javascript class DB { read(id, cb) { cb(null, 'data'); } } const db = new DB(); const read = util.promisify(db.read); // this = undefined inside read ``` Fix: `util.promisify(db.read.bind(db))`. Bind the instance before wrapping. This one causes silent failures in production more often than the callback signature mismatch. **Mistake: expecting a single resolved value when the original passes multiple.** ```javascript const lookup = util.promisify(dns.lookup); lookup('example.com').then(address => console.log(address)); // wrong - address is [address, family] ``` `dns.lookup` calls its callback with `(err, address, family)`. `util.promisify` resolves to an array `[address, family]`. Destructure: `.then(([address, family]) => ...)`. ### Real-world usage - Express: `util.promisify(fs.access)` before serving static files. - MongoDB callback driver (pre-async): `util.promisify(collection.findOne)` in older Lambda handlers. - AWS SDK v2: `util.promisify(s3.getObject)` for S3 operations. - Legacy internal APIs: wrap once at the module boundary, leave callback internals unchanged. ### Follow-up questions **Q:** What is the difference between `util.promisify` and a manual `new Promise` wrapper? **A:** `util.promisify` auto-handles the `(err, value)` pattern and respects `util.promisify.custom`. A manual wrapper gives full control but you write the callback logic yourself. For standard Node.js APIs, `util.promisify` is shorter and less likely to have bugs. **Q:** How does `util.promisify` handle functions that pass multiple values to the callback? **A:** It resolves to an array of all arguments after `err`. For `dns.lookup` that is `[address, family]`. Destructure to get each value separately. **Q:** Can you promisify a synchronous function? **A:** Yes, but there is no point. The Promise resolves immediately and you added overhead for nothing. **Q:** Why avoid `util.promisify` for functions that work with streams? **A:** Streams need backpressure handling. Promisify resolves once and exits, ignoring the ongoing data flow. That can cause memory leaks. Use `stream.pipeline` or async iterators instead. ## Examples ### Callback hell vs promisification Reading two files in sequence with callbacks creates a pyramid. Promisification keeps it flat. ```javascript const fs = require('fs'); const util = require('util'); // Callback version - nesting grows with each sequential call fs.readFile('data.txt', 'utf8', (err, data1) => { if (err) return console.error(err); fs.readFile('data2.txt', 'utf8', (err, data2) => { if (err) return console.error(err); console.log(data1 + data2); // Output: combined file contents }); }); // Promisified - same result, reads top to bottom const readFile = util.promisify(fs.readFile); try { const data1 = await readFile('data.txt', 'utf8'); const data2 = await readFile('data2.txt', 'utf8'); console.log(data1 + data2); // Output: combined file contents } catch (err) { console.error(err); } ``` Same logic, same output. Every sequential step is one line, not one nesting level. ### Express route with promisified file reads A practical pattern: reading user config from disk before sending an API response. ```javascript const fs = require('fs'); const util = require('util'); const readFile = util.promisify(fs.readFile); app.get('/user/:id', async (req, res) => { try { const userData = await readFile(`users/${req.params.id}.json`, 'utf8'); const settings = await readFile('app-settings.json', 'utf8'); res.json({ user: JSON.parse(userData), // Output: user object settings: JSON.parse(settings) // Output: settings object }); } catch (err) { res.status(500).json({ error: err.message }); // Output: { error: 'ENOENT...' } } }); ``` One `try/catch` covers both reads. Without promisification this is two nested callbacks with separate error branches each.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.