Promisify a Callback Function
Write promisify(fn), where fn takes any number of arguments followed by a callback called as callback(error, value).
Return a new function that takes the same arguments without the callback and returns a promise: it rejects with error when that argument is truthy, and resolves with value otherwise.
Examples
const readValue = (key, cb) =>
key ? cb(null, 'value for ' + key) : cb(new Error('key required'));
await promisify(readValue)('a');"value for a"Constraints
- fn always calls its callback exactly once
Notes
- This is what `util.promisify` does in Node, and what wrapping an old browser API looks like.
Hints
Promisify a Callback Function (reference solution)
One way to do it. The Editorial tab walks through why it is written this way.
Solution
1function promisify(fn) {
2 return function (...args) {
3 return new Promise((resolve, reject) => {
4 fn.call(this, ...args, (error, value) => {
5 if (error) reject(error);
6 else resolve(value);
7 });
8 });
9 };
10}Editorial: Promisify a Callback Function
Bridging two eras
Callback APIs pass (error, value) by convention: a truthy first argument means failure. Promisifying is mechanical once you see that the convention maps exactly onto reject and resolve.
Approach
Collect the caller's arguments with a rest parameter, append your own callback, and translate.
Implementation
function promisify(fn) { return function (...args) { return new Promise((resolve, reject) => { fn.call(this, ...args, (error, value) => { if (error) reject(error); else resolve(value); }); }); }; }
Worth knowing
The returned function creates the promise inside itself, not around itself, so nothing runs until it is called.
Node ships this as util.promisify, and it is what wrapping an older browser API looks like. The real-world wrinkle is a callback that fires more than once: a promise ignores every settle after the first, which is usually what you want but hides the bug.