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.execchild_process.execFiledns.lookupdns.lookupServicefs.readfs.writeIf 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 : Erroraddress : stringfamily : integerIts 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()