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.

const {promisify} = require ( 'util' ); const fs = require ( 'fs' ); const readFileAsync = promisify(fs.readFile); 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, ··· }

Using an async function #

The same functionality, but implemented via an async function:

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();

Promisifying functions whose callbacks have more than two parameters #

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));

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.

Custom promisified 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);

Standard functions with custom promisified versions #

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:

A polyfill for older versions of Node.js #

Jordan Harband has created a polyfill for util.promisify() . It works as follows.

Requirements:

Engine must support at least ES5

A global Promise constructor

constructor Warning: still work in progress

Installation:

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);

Further reading #