node.js で DynamoDB に await put(or putitem)する時の正しい方法は？

# node.js でDynamoDBにawait putしエラーを捉える

検索して出てくるのがだいたい以下のコードでawaitした時のエラーの捉えかたの常套手段がわからなかったので書き留めておきます

``` javascript
    dynamodb.put(param, (err, data) => {
        if(err) {
            // エラー処理
        }
    })
```

# パターン１、異常動作例
putが２回実行されてしまう駄目な例です
callbackを呼んでかつpromise()を呼んだ実行をawaitで待ちます、エラーを取る事ができて一見正常動作に見えます。
しかしCALLBACKが２回呼ばれてしまいます、結果的にはそれでも良い場合もあるとは思いますが２倍処理が実行される状況は正常動作とは言えないでしょう。

``` javascript
var aws = require('aws-sdk');
//var dynamodb = new aws.DynamoDB({region: 'ap-northeast-1'});// putItem は {"S": "hoge"} という構造体を要求されて面倒なので使わない
var dynamodb = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});// 型指定無しで put を使いたいのでこちらを採用

// 正常動作しない、putが２回実行されている
async function Write1() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    await dynamodb.put(param, (err, data) => {
        console.log("###CALLBACK"); // ２回呼ばれてしまう、おそらく後の promise のせい
        Err = err;
    }).promise(); // おそらく promise() のせいで CALLBACK が２回呼ばれている、がpromiseしないと await できない
    
    if(Err) {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    } else {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    }
}

async function main(event, context) {
    return Write1();
}

exports.handler = async (event, context) => {
    return await main(event, context);
};

```

## パターン１に関するネット上の情報
[stack overflowより](https://stackoverflow.com/questions/64902337/aws-lambda-dynamodb-putitem-executed-twice-always)

雑に解釈すると「非同期呼び出し解決のために競合する呼び出し方を複数持っている、そのためコールバック、promiseを同時に使うと２回実行されてしまう」という事みたいです。

以下、パターン２、パターン３で正常動作例を示します、パターン２が正解なんじゃないかと思います。

# パターン２、正常動作例
前後のソースを省き関数部分のみとします、実験ソース全体は最後に記します。
コールバックを使わずにpromiseだけで解決します、多分これが正解だと思います

``` javascript
// 正常動作、promise
async function Write2() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    return await dynamodb.put(param).promise()
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}
```

# パターン３、正常動作例
前後のソースを省き関数部分のみとします、実験ソース全体は最後に記します。
Promise内でコールバックを使います
パターン２をわざわざ焼き直したという感じでメリットを感じないですね、パターン２が正解なんだと思います。

``` javascript
// 正常動作、promise を作成する
async function Write3() {
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    return await new Promise((resolve, reject) => {
        dynamodb.put(param, (err, data) => {
            console.log("###CALLBACK");
            if(err) {
                reject(err);
            } else {
                resolve(true);
            }
        });
    })
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}
```



# 実験ソース全体

``` javascript

var aws = require('aws-sdk');
//var dynamodb = new aws.DynamoDB({region: 'ap-northeast-1'});// putItem は {"S": "hoge"} という構造体を要求されて面倒なので使わない
var dynamodb = new aws.DynamoDB.DocumentClient({region: 'ap-northeast-1'});// 型指定無しで put を使いたいのでこちらを採用

// 正常動作しない、恐らくputが２回実行されている
async function Write1() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    await dynamodb.put(param, (err, data) => {
        console.log("###CALLBACK"); // ２回呼ばれてしまう、おそらく後の promise のせい
        Err = err;
    }).promise(); // おそらく promise() のせいで CALLBACK が２回呼ばれている、がpromiseしないと await できない
    
    if(Err) {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    } else {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    }
}

// 正常動作、promise
async function Write2() {
    let Err = null;
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    return await dynamodb.put(param).promise()
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

// 正常動作、promise を作成する
async function Write3() {
    let param = {
        "TableName": "test-table1",
        "Item": {
            "test-key": "A",
            "parama1": "abcdefg",
            "parama2": 123,
        }
    };
    
    return await new Promise((resolve, reject) => {
        dynamodb.put(param, (err, data) => {
            console.log("###CALLBACK");
            if(err) {
                reject(err);
            } else {
                resolve(true);
            }
        });
    })
    .then(() => {
        return {
            statusCode: 200,
            body: JSON.stringify("OK"),
        };
    })
    .catch((err) => {
        return {
            statusCode: 500,
            body: JSON.stringify("NG"),
        };
    });
}

async function main(event, context) {
    //return Write1();
    return Write2();
    //return Write3();
}

exports.handler = async (event, context) => {
    return await main(event, context);
};

if(!module.parent) {
    event = {
        "body-json": {}
    }
    
    new Promise(async () => {
        let contents = await main(event, null);
        console.log("###############main: contents", contents);
        return true;
    });
}

```