Node.js 8.0.0 から promisify が使えるようになった

Node.js 8.0.0 から util.promisify が使えるようになった。

これで、外部ライブラリを使わなくても promisify ができる。

const { promisify } = require('util')
const { readFile } = require('fs')

const readFileAsync = promisify(readFile)

async function main () {
  let buffer = await readFileAsync('hoge.txt')
  let text = buffer.toString()
  console.log(text)
}

main()

Node.js 7 までは、 bluebird などのライブラリを使って promisify していた。しかも、 async/await が使えない時代だったら、 co ライブラリとかで代用していた。

// 昔の書き方
const { promisify } = require('bluebird')
const { readFile } = require('fs')
const co = require('co')

const readFileAsync = promisify(readFile)

function main () {
  return co(function * () {
    let buffer = yield readFileAsync('hoge.txt')
    let text = buffer.toString()
    console.log(text)
  })
}

main()

もうこういう書き方はしなくて済むわけですね。

promisify は、引数にコールバック関数を渡すような非同期の関数を Promise 化する。

const { promisify } = require('util')

function wait (second, callback) {
  setTimeout(() => {
    callback()
  }, 1000 * second)
}

// Promise 化された関数を返す
const waitAsync = promisify(wait)

async function main () {
  console.time('timer')
  await waitAsync(2)
  console.timeEnd('timer')
}

main()

// 出力
// timer: 2003.266ms

コールバック関数には規則があって、第一引数は常にエラー扱いになる。

function wait (second, callback) {
  setTimeout(() => {
    callback('result') // これだと常にエラーが起きる
  }, 1000 * second)
}
const waitAsync = promisify(wait)

async function main () {
  await waitAsync(2)
}
main()

// 出力
// UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): result

何か結果を返したい場合には、コールバック関数の第二引数に与える。

function wait (second, callback) {
  setTimeout(() => {
    callback(null, 'result') // 結果を返したい場合にはこう書く
  }, 1000 * second)
}