Lambda→DynamoDBが非同期処理と知らないやつがとった行動


Alexaが人を返してくれない

DynamoDBからあるスキルに詳しい人の名前を取得してAlexaに返してもらおうとしている。DynamoDBへアクセスはできているが、なぜか人の名前を返してくれず、「さん」しか出てこない。

修正前のソースはこちら。

const SearchSkillHandler = {
  canHandle(handlerInput) {
    console.log('called SearchSkillHandler.canHandle');
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest'
           && request.intent.name === 'SearchSkillIntent';
  },
  handle(handlerInput) {
    const skillsNameValue = getSlotValue(handlerInput.requestEnvelope, 'skills');
    console.log('called SearchSkillHandler.handle');

    const params = {
        TableName: 'skillmap',
        KeyConditionExpression: 'skill = :skill',
        ExpressionAttributeValues: {
            ':skill': `${skillsNameValue}`
        }
    };

    let skillsPersonName = '';
    documentClient.query(params, (err, data) => {
        if (err) {
            console.log(JSON.stringify(err, null, 2));
        } else {
            console.log(JSON.stringify(data, null, 2));
            console.log('取得したもの name : ' + data.Items[0].name);
            console.log('取得したもの skill : ' + data.Items[0].skill);
            skillsPersonName = data.Items[0].name;
            console.log('取得したもの skillsPersonName1 : ' + skillsPersonName);
        }
    });

    console.log('取得したもの skillsPersonName2 : ' + skillsPersonName);

    return handlerInput.responseBuilder
            .speak(`${skillsNameValue}` + "" + `${skillsPersonName}` + "さんが詳しいです。")
            .getResponse();
  }
};

ログを追ってみた

困ったときはログ。ということで、CloudWatchのログを見てみました。

よく見てみると、skillsPersonName2のほうがskillsPersonName1より早くログにでている。たぶん、DynamoDBからのデータ取得処理は非同期処理なんだろう。

非同期処理という仮説に基づき対応

修正後のソースはこちら。

const SearchSkillHandler = {
  canHandle(handlerInput) {
    console.log('called SearchSkillHandler.canHandle');
    const request = handlerInput.requestEnvelope.request;
    return request.type === 'IntentRequest'
           && request.intent.name === 'SearchSkillIntent';
  },
  async handle(handlerInput) {
    const skillsNameValue = getSlotValue(handlerInput.requestEnvelope, 'skills');
    console.log('called SearchSkillHandler.handle');

    const params = {
        TableName: 'skillmap',
        KeyConditionExpression: 'skill = :skill',
        ExpressionAttributeValues: {
            ':skill': `${skillsNameValue}`
        }
    };

    let skillsPersonName = '';
    await documentClient.query(params, (err, data) => {
        if (err) {
            console.log(JSON.stringify(err, null, 2));
        } else {
            console.log(JSON.stringify(data, null, 2));
            console.log('取得したもの name : ' + data.Items[0].name);
            console.log('取得したもの skill : ' + data.Items[0].skill);
            skillsPersonName = data.Items[0].name;
            console.log('取得したもの skillsPersonName1 : ' + skillsPersonName);
        }
    }).promise();

    console.log('取得したもの skillsPersonName2 : ' + skillsPersonName);

    return handlerInput.responseBuilder
            .speak(`${skillsNameValue}` + "" + `${skillsPersonName}` + "さんが詳しいです。")
            .getResponse();
  }
};

handleの関数にasyncをつけたのと、documentClient.queryにawaitとpromiseをつけて同期処理にしました。

うまくいった!!

ちゃんと返してくれました!ログをみて仮説を立てて、対応という基本の動きにそって動けた!!