今更だけどAWS Lambdaのnode(Javascript)で Promiseしてみた
はじめに
node書いてるとコールバック地獄やですよね!
async/await使いたかったんだけど、AWS Lambdaなので、おとなしくPromiseしてみました
コールバック地獄
普通に書くとコールバック地獄になってしまう
'ues strict'; const AWS = require('aws-sdk'); const DDB = new AWS.DynamoDB(); const DDBDC = new AWS.DynamoDB.DocumentClient(); exports.handler = function(event, context, callback) { DDBDC.get( { TableName: "table1", Key:{"id": 1}} , (err1, data1) => { if (err1) { callback(err1); return; } DDBDC.get( { TableName: "table2", Key:{"id": 1}} , (err2, data2) => { if (err2) { callback(err2); return; } DDBDC.get( { TableName: "table3", Key:{"id": 1}} , (err3, data3) => { if (err3) { callback(err3); return; } // data1,data2,data3を使い データを操作 }); }); }); }
おなじみのコールバック地獄
table1の検索後にtable2、table3と同期的に処理を行い結果を得られます
まず コールバック地獄辛い
そして table1、table2、table3 の処理を非同期で同時に行ってもよい
ただし、データを使う時には全部が終了していなければならない
ぱっと知ってる技術では 考えつかなかった
'ues strict'; const AWS = require('aws-sdk'); const DDB = new AWS.DynamoDB(); const DDBDC = new AWS.DynamoDB.DocumentClient(); exports.handler = function(event, context, callback) { DDBDC.get( { TableName: "table1", Key:{"id": 1}} , (err1, data1) => { if (err1) { callback(err1); return; } }); DDBDC.get( { TableName: "table2", Key:{"id": 1}} , (err2, data2) => { if (err2) { callback(err2); return; } }); DDBDC.get( { TableName: "table3", Key:{"id": 1}} , (err3, data3) => { if (err3) { callback(err3); return; } }); // data1,data2,data3の同期を待ち データを操作 }
table1、table2、table3の検索が全て終わったのはどうやって調べましょうか(誰か教えて)
それ、Promise使えばいいよ
Promiseを使えば非同期関数を綺麗に(階層深くせず)コールバックを隠蔽できます
例えば最初の 各テーブルの読み込みを同期する場合は
'ues strict'; const AWS = require('aws-sdk'); const DDB = new AWS.DynamoDB(); const DDBDC = new AWS.DynamoDB.DocumentClient(); var get1 = (id) => { return DDBDC.get( { TableName: "table1", Key:{"id": id}}).promise(); }; var get2 = (id) => { return DDBDC.get( { TableName: "table2", Key:{"id": id}}).promise(); }; var get3 = (id) => { return DDBDC.get( { TableName: "table3", Key:{"id": id}}).promise(); }; exports.handler = function(event, context, callback) { get1(1) .then(get2(1)) .then(get3(1)) .then(()=>{ // data1,data2,data3を使い データを操作 }) .catch( (err) => { callback(err); }); }
こんな感じで(動かしてないから間違えてるかもしれないけど) table1をまちtable2、table3と同期して
実行可能です
細かい実装はみてないけど、ぱっと見でいうと
AWSのライブラリにはだいたい promise()メソッドがある
中身は new Promise()で 関数からPromiseを作る便利メソッド
Promiseのメソッド thenは、関数の実行を待ち、終了後に引数の関数を呼び出す
結果的に get1の終了後にget2が呼ばれ、その後 get3が呼ばれる
では、お互いの終了を待たず get1、get2、get3を並列で実行し、全てが終了するのを待つ場合はどうだ?
並列で実行されるため、こっちの方が圧倒的にパフォーマンスが良い
promise all 使おう
'ues strict'; const AWS = require('aws-sdk'); const DDB = new AWS.DynamoDB(); const DDBDC = new AWS.DynamoDB.DocumentClient(); var get1 = (id) => { return DDBDC.get( { TableName: "table1", Key:{"id": id}}).promise(); }; var get2 = (id) => { return DDBDC.get( { TableName: "table2", Key:{"id": id}}).promise(); }; var get3 = (id) => { return DDBDC.get( { TableName: "table3", Key:{"id": id}}).promise(); }; exports.handler = function(event, context, callback) { Promise.all([get1(1), get2(1), get3(1)]) .then ((results) => { var data1 = results[0]; var data2 = results[1]; var data3 = results[2]; // data1,data2,data3を使い データを操作 }).catch((err)=>{ callback(err); }); }
Promise.all に配列としてpromiseを返す関数を渡せば
thenにて全ての終了を同期出来る
そして結果は 配列として帰ってくる
とてもすばらしい!
これでコールバックとはおさらば!
AWS Lambdaで使えるnodeのバージョンがあがれば、さらに綺麗にかける async/awaitも使えるんだけどね・・