知らない間にAWS_SDKのコールバック地獄から解放されてた(for JavaScript)

※ この記事は元々Qiitaへ投稿したものの移植です

今までJavaScriptaws-sdkを扱う場合は
↓こんな感じでコールバック関数を渡していました。

■元々

s3.listBuckets((err, data) => {
  if (err) {
    console.error(err);
  } else {
    console.log(data);
  }
});

簡単な処理の場合はこの書き方で良いと思うのですが 処理を手続き的に書く際にとても苦労します・・・

■いわゆるコールバック地獄

dynamoDB.getItem({/*省略*/}, (err, data) => {
  if (err) {
    console.error(err); // エラーもネストの数だけハンドリングが必要
  } else {
    s3.getObject({/*省略*/}, (err, data) => {
      if (err) {
        console.error(err);
      } else {
        console.log(data);
      }
    });
  }
});

これを回避するためにasync/awaitを使おうとすると すべてのApi呼び出しをPromiseでラップする関数を定義する必要がありました。

■ラップした例

// これはこれでコールバック地獄
// しかも使用するApiの数だけ用意する・・・
function getItem(opts) {
  return new Promise((resolve, reject) => {
    dynamoDB.getItem(opts, (err, data) => {
      if (err) reject(err);
      else     resolve(data);
    });
  });
}

// メイン処理
// ここはきれいに書ける
async function hoge() {
  try {
    const res1 = await getItem({/*省略*/});
    const res2 = await getObject({/*省略*/});
    console.log(res2);
  } catch(err) {
    console.error(err); // エラーもまとめてハンドリングできる
  }
}

これを回避するためにオレオレPromisifyなども作ったりしました。

コールバック地獄にも終わりが!

最近だと↓こう記述できるとのこと

async function hoge() {
  try {
    const res1 = await dynamoDB.getItem({/*省略*/}).promise();
    const res2 = await s3.getObject({/*省略*/}).promise();
    console.log(res2);
  } catch(err) {
    console.error(err);
  }
}

見ての通りApi呼び出し後の戻り値から そのままPromiseが返却できるらしいのです。

このpromise()関数はAWS.Requestの実装なので 例に限らずほぼ全てのAPIで使用可能だと思われます。(※ 確認したわけではありません)

まとめ

調べてみると2016年のLambdaがNode.v4対応した頃には実装されていたようですが、 公式のサンプルもコールバックを渡すプログラムになってたりするので このpromiseの存在を知らない人もまだいるのではないでしょうか?(まさか僕だけ・・・?

ぜひPromiseを使ってシンプルな非同期処理を書いてきましょう!

参考記事

https://qiita.com/toshihirock/items/4e4231c04332c209e31d