結論
handlerをasyncにして普通にreturnで99.9%くらいOK
async + return
最も普通な方法です.
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
async + returnでsetTimeoutする
exports.handler = async (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
setTimeout(() => {
console.log("1000ms後");
}, 1000);
setTimeout(() => {
console.log("2000ms後");
}, 2000);
return response;
};
のような場合, 1回目の実行でreturnするまでが1000ms未満の場合, console.logには出力されず, ウォームスタートした場合に次回以降出力されます.
1回目
START RequestId: 4978d8d6-0550-46d2-ad36-769858d22fa3 Version: $LATEST
END RequestId: 4978d8d6-0550-46d2-ad36-769858d22fa3
REPORT RequestId: 4978d8d6-0550-46d2-ad36-769858d22fa3 Duration: 2.50 ms Billed Duration: 3 ms Memory Size: 128 MB Max Memory Used: 85 MB Init Duration: 391.34 ms
2回目 (2秒以上開ける)
START RequestId: e4dcdbb1-a150-43b4-9e80-c7a776837c16 Version: $LATEST
2021-09-01T03:17:49.058Z 4978d8d6-0550-46d2-ad36-769858d22fa3 INFO 1000ms後
2021-09-01T03:17:49.059Z 4978d8d6-0550-46d2-ad36-769858d22fa3 INFO 2000ms後
END RequestId: e4dcdbb1-a150-43b4-9e80-c7a776837c16
REPORT RequestId: e4dcdbb1-a150-43b4-9e80-c7a776837c16 Duration: 160.53 ms Billed Duration: 161 ms Memory Size: 128 MB Max Memory Used: 85 MB
console.logで出力される際についてくる実行IDが, 前回のものになっていることがわかります. このsetTimeout内のconsole.log
は, Lambda実行後かつhandler実行前に実行されます.
(setTimeout(() => {console.log(new Date());}, 1000);
してみましょう)
3回目実行直後に4回目
START RequestId: a87c55c4-73b6-402b-9b51-924d7ca7e178 Version: $LATEST
END RequestId: a87c55c4-73b6-402b-9b51-924d7ca7e178
REPORT RequestId: a87c55c4-73b6-402b-9b51-924d7ca7e178 Duration: 1.14 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 86 MB
ウォームスタートしていますが, 前回の実行のsetTimeoutが完了していないため, 何も出力されません.
5回目(2秒以上待機)
START RequestId: 20217709-e518-48fe-ad60-0fe1c7dd627e Version: $LATEST
2021-09-01T03:19:13.790Z a87c55c4-73b6-402b-9b51-924d7ca7e178 INFO 1000ms後
2021-09-01T03:19:13.790Z a87c55c4-73b6-402b-9b51-924d7ca7e178 INFO 1000ms後
2021-09-01T03:19:13.790Z a87c55c4-73b6-402b-9b51-924d7ca7e178 INFO 2000ms後
2021-09-01T03:19:13.790Z a87c55c4-73b6-402b-9b51-924d7ca7e178 INFO 2000ms後
END RequestId: 20217709-e518-48fe-ad60-0fe1c7dd627e
REPORT RequestId: 20217709-e518-48fe-ad60-0fe1c7dd627e Duration: 1.47 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 86 MB
溜まっていたsetTimeoutが処理された後にLambdaを実行したため, 一気に表示されました.
詳しくはLambdaのライフサイクルで
非async + Promise
asyncで普通にreturnすれば値を返せるので, 当然Promiseを返すとうまくいきます.
exports.handler = (event) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return new Promise((resolve) => {
resolve(response)
});
};
非asyncな場合, 普通にreturnするだけだとnullになります.
callback
handlerの第3引数のcallbackで返す方法です. asyncかは問いません.
exports.handler = async (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
callback(null, response);
// callback後も実行される
console.log("callback後");
setTimeout(() => {
response.statusCode = 400; // ここの実行後にcallbackに入れた値が返るため, 400が出力される
}, 1000);
};
この場合, setTimeoutなどの非同期処理が全て終わってからcallbackに入れた値が返されます.
複数回callbackした場合
最も最初のcallbackのみ利用されます.
exports.handler = async (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
setTimeout(() => {
callback(null, response);
}, 1000);
callback(null, "hoge");
callback(null, "fuga");
};
hoge
エラーの場合は第1引数
callback(new Error("era-"));
context
context.done
, context.succeed
, context.fail
が使えます.
exports.handler = async (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
context.succeed(response);
setTimeout(() => {
console.log("fuga"); // この時点では実行されない (次回実行時にconsole.logされている)
}, 1000);
console.log("hogehoge"); // 実行される
};
つまり, 非同期は後回しになるものの, succeed
以降も処理が続く, returnとcallbackの中間の性質を持っています. ぶっちゃけ使いません. async + returnでいいです.
callback, context, return全部並べてみる
普通に最初のものが処理されます.
exports.handler = async (event, context, callback) => {
context.succeed("hoge");
callback(null, "fuga");
setTimeout(() => {
console.log("a"); // 次回までお預け
}, 1000);
return "hige";
};
hoge
exports.handler = async (event, context, callback) => {
callback(null, "fuga");
context.succeed("hoge");
setTimeout(() => {
console.log("a"); // 実行される
}, 1000);
return "hige";
};
fuga
どのみちこんな書き方したらコードレビューで凄まじい数のchange requestがやってくるはずなので, 普通書かないです.
さいごに
他にも面白いのあったら教えてください🙏