util.callbackify()を使用しよう

こんにちは、北野です。

今回の投稿は前回記事(util.promisify()を活用しよう)と対となる
「util.callbackify()を使用しよう」
です。
それでは行ってみましょう。

util.callbackify()とは?

node v8で追加されたutilモジュールの関数の1つで、Promiseスタイルの関数を、
コールバックスタイルの関数にラップします。
(Promiseスタイルの関数とは、非同期処理の結果をPromiseで受け取る形式の関数、
コールバックスタイルの関数とは、非同期処理の結果をコールバックで受け取る形式の関数、のことです)

例) util.callbackify()の使用例

const util = require('util');

// Promiseスタイルの関数
function PromiseStyleFunction() {
  return Promise.resolve(10);
}

// コールバックスタイルにラップ
let callbackStyleFunction = util.callbackify(PromiseStyleFunction);

util.callbackify()はPromiseを返す関数に対して使用することができます。

また、コールバック関数は以下のような引数を取ります。

第一引数:err(エラー)
第二引数:data(データ)

例)

function callback(err, data) {
}

 

util.callbackify()を使用したサンプルコード

動作確認環境

今回の動作確認環境は以下となります。

確認環境 バージョン
nodejs v8.10.0

util.callbackify()でラップした関数の成功例

Promiseスタイルの関数の処理が成功した場合の例になります。

以下の例ではまず、Promiseスタイルの関数を定義し、それをコールバックスタイルの関数にラップしています。
その後、ラップした関数を実行し、戻ってきた結果をコンソールに表示しています。
例)

const util = require('util');

// Promiseスタイルの関数を定義
function PromiseStyleFunction() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(10);
    }, 
    3000);
  });
}

// コールバックスタイルの関数にラップ
let callbackStyleFunction = util.callbackify(PromiseStyleFunction);

// コールバックスタイルの関数を実行
callbackStyleFunction((err, data) => {
  console.log('data: ' + data);
});

上記の実行結果はプログラム実行3秒後に

err: null
data: 10

を表示します。

util.callbackify()でラップした関数の失敗例

Promiseスタイルの関数の処理が失敗した場合の例になります。

以下の例ではまず、Promiseスタイルの関数を定義し、それをコールバックスタイルの関数にラップしています。
その後、ラップした関数を実行し、戻ってきた結果をコンソールに表示しています。
例)

const util = require('util');

// Promiseスタイルの関数
function PromiseStyleFunction() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('エラーが発生しました'));
    }, 
    3000);
  });
}

// コールバックスタイルの関数にラップ
let callbackStyleFunction = util.callbackify(PromiseStyleFunction);

// コールバックスタイルの関数を実行
callbackStyleFunction((err, data) => {
  console.log('err: ', err);
  console.log('data: ' + data);
});

上記の実行結果はプログラム実行3秒後に

err: Error: エラーが発生しました
at Timeout.setTimeout [as _onTimeout] (C:\blog\main.js:6:14)
at ontimeout (timers.js:482:11)
at tryOnTimeout (timers.js:317:5)
at Timer.listOnTimeout (timers.js:277:5)
data: undefined

を表示します。

クラス内で定義した関数にutil.callbackify()を使用する場合の使用例

クラス内で定義した関数にutil.callbackify()を使用した例となります。

クラス内で定義した関数にthisを使用していない場合は上記までの方法で問題ありませんが、thisを使用している場合はそのまま使用するとエラーが発生します。

例)

const util = require('util');

class TestClass {
  constructor() {
    this.name = 'dog';
  }

  // Promiseスタイルの関数
  promiseStyleFunction() {
    console.log('name: ' + this.name);
    
    return Promise.resolve();
  }
}

// クラスをインスタンス化
let testClass = new TestClass();

// コールバックスタイルの関数にラップする
let callbackStyleFunction = util.callbackify(testClass.promiseStyleFunction);

// コールバックスタイルの関数を実行
callbackStyleFunction((err, data) => {
  console.log('err: ', err);
  console.log('data: ', data);
});

上記の実行結果は

C:\blog\main.js:10
    console.log('name: ' + this.name);
                                ^
TypeError: Cannot read property 'name' of undefined
    at promiseStyleFunction (C:\blog\main.js:10:33)
    at promiseStyleFunction (util.js:1088:13)
    at Object.<anonymous> (C:\blog\main.js:22:1)
    at Module._compile (module.js:652:30)
    at Object.Module._extensions..js (module.js:663:10)
    at Module.load (module.js:565:32)
    at tryModuleLoad (module.js:505:12)
    at Function.Module._load (module.js:497:3)
    at Function.Module.runMain (module.js:693:10)
    at startup (bootstrap_node.js:188:16)

となります。

これを回避するためのは上記のソースを次のようにしてthisオブジェクトを事前にバインドします。

testClass.promiseStyleFunction

↓クラスの関数.bind(インスタンス変数)の形に変更

testClass.promiseStyleFunction.bind(testClass)

bind()関数についてはいくつかの使い方があるのですが、今回はthisを固定化(バインド)するために使用しています。

今回の場合、bind()関数の戻り値はthisが固定化された関数オブジェクトとなります。

修正版のサンプルは以下のようになります。

例)

const util = require('util');

class TestClass {
  constructor() {
    this.name = 'dog';
  }

  // Promiseスタイルの関数
  promiseStyleFunction() {
    console.log('name: ' + this.name);
    
    return Promise.resolve();
  }
}

// クラスをインスタンス化
let testClass = new TestClass();

// コールバックスタイルの関数にラップする
let callbackStyleFunction = 
    util.callbackify(testClass.promiseStyleFunction.bind(testClass));

// コールバックスタイルの関数を実行
callbackStyleFunction((err, data) => {
  console.log('err: ', err);
  console.log('data: ', data);
});

実行結果は

name: dog
err: null
data: undefined

となり、意図通りの出力となりました。

外部のライブラリ等を使用する場合、このケースに当てはまる場合もありますので、変数が見つからないの等のエラーになった場合は一度bind()関数でthisの固定化を行ってみると解決するかもしれません。

 

今回のutil.callbackify()や前回記事にしたutil.promisify()を使用することで、コールバックスタイルの関数とPromiseスタイルの関数との相互変換が可能になります。

過去に作成したライブラリの再利用等や標準で用意されている関数に使用すると作業効率が上がりそうですね!

今回の記事は以上となります。
お疲れさまでした。

次回はJavaScriptにおけるthisについてを予定しています。

それではまた、次の記事にて!

コメント

株式会社CONFRAGE ITソリューション事業部をもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む

タイトルとURLをコピーしました