TORIPIYO DIARY

recent events, IoT, programming, security topics

Nodejsでコールバックで処理する前提の非同期関数をプロミスオブジェクトを返す関数に変換する例

例えば、以下のような形で、asyncCallbackFunctionという関数の中でコールバックを受け取る非同期の関数fs.readFileを実行することになったとします。

async function asyncCallbackFunction(path) {

  fs.readFile(path, 'utf-8', (err, data) => {
    if (err) {
      throw err;
    }
    console.log(`${path} is loaded!`); // #1
  });

  console.log('task finished!') // #2
}

asyncCallbackFunction('/etc/passwd');

fs.readFile関数は、非同期にコールバック処理をする関数です。上の例のコードでは、fs.readFileは非同期に実行されるので、 このままでは、#2 => #1 の順番に実行されます。

もし、fs.readFileが、コールバック処理を省略するとPromiseオブジェクトを返す関数であれば、awaitを利用して以下のように書き換えることで、#1 => #2の順番で実行することができます。

async function asyncCallbackFunction(path) {

  try {
    const data = await fs.readFile(path, 'utf-8');
    console.log(`${path} is loaded!`); // #1
  } catch(err) {
    throw err;
  }

  console.log('task finished!') // #2
}

asyncCallbackFunction('/etc/passwd');

しかし、これを実行すると、UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received 'utf-8'というエラーが出てしまい、実行することができません。fs.readFile(path, 'utf-8')では、promiseオブジェクトを返してくれないようです。

このような非同期のコールバック処理前提の関数を、async/awaitで処理できるようにするためには、以下のようにfs.readFile関数をpromiseオブジェクトを返すようにラップしてあげます。

function promiseReadFile(path, enc) {
  return new Promise((resolve, reject) => {
    fs.readFile(path, enc, (err, data) => {
      if(err) {
        reject(err);
      }
      resolve(data);
    });
  });
}

promiseオブジェクトを返すpromiseReadFile関数を利用することで、#1 => #2 の順番で処理を実行することが出来るようになりました。

async function asyncCallbackFunction(path) {

  try {
    const data = await promiseReadFile(path, 'utf-8');
    console.log(`${path} is loaded!`); // #1
  } catch(err) {
    throw err;
  }

  console.log('task finished!') // #2
}

asyncCallbackFunction('/etc/passwd');