util.promisify()
Node.js 8 has a new utility function: util.promisify()
. It converts a callback-based function to a Promise-based one.
util.promisify()
in action If you hand the path of a file to the following script, it prints its contents.
// echo.js
const {promisify} = require('util');
const fs = require('fs');
const readFileAsync = promisify(fs.readFile); // (A)
const filePath = process.argv[2];
readFileAsync(filePath, {encoding: 'utf8'})
.then((text) => {
console.log('CONTENT:', text);
})
.catch((err) => {
console.log('ERROR:', err);
});
Note how, in line A, the script uses promisify()
to convert the callback-based function fs.readFile()
to the Promise-based function readFileAsync()
.
The following interaction shows how the script is used:
$ node echo.js echo.js
CONTENT: const {promisify} = require('util');
···
$ node echo.js unknown.txt
ERROR: { Error: ENOENT: no such file or directory, ··· }
The same functionality, but implemented via an async function:
// echoa.js
const {promisify} = require('util');
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);
const filePath = process.argv[2];
async function main() {
try {
const text = await readFileAsync(filePath, {encoding: 'utf8'});
console.log('CONTENT:', text);
}
catch (err) {
console.log('ERROR:', err);
}
}
main();
The callbacks of the following functions receive more than one result value (in addition to the error value):
child_process.exec
child_process.execFile
dns.lookup
dns.lookupService
fs.read
fs.write
If you promisify one of these functions, it returns an object of values (not a single value). For example, the callback of dns.lookup()
has the following callback parameters:
err : Error
address : string
family : integer
Its promisified version fulfills its Promise with an {address, family}
object:
const util = require('util');
const dns = require('dns');
const lookupAsync = util.promisify(dns.lookup);
lookupAsync('nodejs.org')
.then(obj => console.log(obj));
// { address: '104.20.23.46', family: 4 }
promisify()
handles non-standard callbacks via the internal symbol internal/util/customPromisifyArgs
. Given that non-standard callbacks are not recommended, you should never need this mechanism for your own functions.
The API of promisified
comes with the symbol util.promisify.custom
, which lets you attach a promisified version to a callback-based function. In the following example, fooAsync
is the promisified version of foo
.
const util = require('util');
function foo() {
return 'abc';
}
async function fooAsync() {
return 'abc';
}
foo[util.promisify.custom] = fooAsync;
console.log(util.promisify(foo) === fooAsync); // true
At the moment, two standard functions have promisified versions:
> setImmediate[util.promisify.custom]
[Function]
> setTimeout[util.promisify.custom]
[Function]
promisify()
needs help here, because these functions have the callback as the first parameter – against Node.js conventions:
Jordan Harband has created a polyfill for util.promisify()
. It works as follows.
Requirements:
Promise
constructorInstallation:
npm install util.promisify
There are two main ways of using this polyfill.
First: retrieve either the built-in implementation (Node 8) or a polyfill (older versions of Node).
const promisify = require('util.promisify');
const fs = require('fs');
const readFileAsync = promisify(fs.readFile);
Second: patch module util
on older versions of Node.
const util = require('util');
require('util.promisify').shim();
const fs = require('fs');
const readFileAsync = util.promisify(fs.readFile);
util.promisify()