AWS Lambda関数でNode.jsを使用し、forEach()でasync/awaitしたい状況に陥ったのですが、コードを書いて実行してもawaitの文が実行されていませんでした。色々調べたところ、Node.jsの**array-foreach-async
**というライブラリを利用することで簡単に解決することができたので、まとめていきます。
環境
- AWS Lambda
- Node.js 12
修正前の実行コード
今回の説明において不要なコードは省略しています。
const env = process.env
const requestPromise = require('request-promise');
const AWS = require('aws-sdk');
const DB = new AWS.DynamoDB.DocumentClient();
exports.handler = async(event, context) => {
// (省略)
event.Records.forEach(async(record) => { // ここでasync
// (省略)
const data = await DB.put(dbParams).promise(); // 1つ目のawait
console.log(data);
// (省略)
const req = await requestPromise.post(options); // 2つ目のawait
console.log(req);
});
return "success!"
};
実行すると、出力にはsuccess!
のみが表示され、console.logは無視されます。forEach()がawaitの処理完了を待たずに終了し、Lambda関数が閉じてしまったためです。どちらか一方のawaitをコメントアウトしても結果は変わらず。forEach()ではasync/awaitが効いていないことが分かります。
そもそもArray.prototype.forEach()
自体がasync関数ではないため、引数のcallback関数にasync/awaitを付けたところで無意味ということです。
修正後の実行コード
Node.jsの**array-foreach-async
**というライブラリをインストールすることで、forEach()をasync関数にしたforEachAsync()
を使用できるようになります。
これを使えば、forEach()と同じ動作でasync/awaitを実現できます(参考:array-foreach-async - npm)。
まずはライブラリをインストールします。
npm install array-foreach-async
ソースコードは次のようになります。
const env = process.env
const requestPromise = require('request-promise');
const AWS = require('aws-sdk');
const DB = new AWS.DynamoDB.DocumentClient();
require('array-foreach-async'); // ライブラリの読み込み
exports.handler = async(event, context) => {
// (省略)
await event.Records.forEachAsync(async(record) => { // 変更箇所
// (省略)
const data = await DB.put(dbParams).promise();
console.log(data);
// (省略)
const req = await requestPromise.post(options);
console.log(req);
});
return "success!"
};
forEach
を**forEachAsync
**に変更し、文の先頭にawaitを付けます。
実行すると、awaitの文が両方ともきちんと実行され、ログの出力を確認できました。
補足
ずっとforEach()を使うことしか考えていなかったため後から気が付いたのですが、そもそもforEach()の代わりにfor〜of
を使用すれば解決できました。JavaScriptに慣れていなさすぎる...
修正前の実行コードを次のように変更すればOKです。
const env = process.env
const requestPromise = require('request-promise');
const AWS = require('aws-sdk');
const DB = new AWS.DynamoDB.DocumentClient();
exports.handler = async(event, context) => {
// (省略)
for(const record of event.Records) {
// (省略)
const data = await DB.put(dbParams).promise(); // 1つ目のawait
console.log(data);
// (省略)
const req = await requestPromise.post(options); // 2つ目のawait
console.log(req);
}
return "success!"
};
コードも簡潔であり、わざわざ**array-foreach-async
**をインストールする必要がないので、どうしてもforEach()を使わなければならない状況以外では、素直にfor〜of
を使う方が良いかもしれませんね。
ご参考までに。