0
votes

I have a Lambda function for an Alexa skill which is written in Nodejs. It makes an HTTP call to a service and returns the output to the Alexa Skill. When the skill is invoked, the lambda is called, and the HTTP call is made. However, before the HTTP response is returned, the lambda returns, hence the skill does not get an answer.

Below is my Lambda function

var Alexa = require('ask-sdk');
var http = require('http');
var SKILL_NAME = 'Some Facts';

var GetNewFactHandler = {
  canHandle(handlerInput) {
  var request = handlerInput.requestEnvelope.request;
  return request.type === 'LaunchRequest'
  || (request.type === 'IntentRequest'
    && request.intent.name === 'GetNewFactIntent');
},
handle(handlerInput) {
    getTop("", (data)=>{
    let speechOutput = "The result is " + data;
        console.log("resp from lambda ", speechOutput)
        var respSpeak =  handlerInput.responseBuilder
        .speak(speechOutput)
        .withSimpleCard(SKILL_NAME, data)
        .getResponse();
        console.log("respSpeak ", respSpeak);
        return respSpeak;
    });
  },
};


function getTop(query, callback) {
   var options = {
        host: 'somehost',
        port: '8100',
        path: '/top',
        method: 'GET',
   };

    var req = http.request(options, res => {
        res.setEncoding('utf8');
        var responseString = "";

        res.on('data', chunk => {
            responseString = responseString + chunk;
        });

        res.on('end', () => {
            console.log("********",JSON.parse(responseString).Name);
            let respStr = JSON.parse(responseString).Name;
            callback(respStr);
        });

    });
    req.end();
}

In the lambda logs, I can see the logs in getTop(). But the response of the lambda returns before the response of the HTTP call is received. I was thinking that building the response in the callback would ensure that the response is returned only after the HTTP call completes. But that doesn't seem to be the case. How can this be fixed? Any help is appreciated.

1
Depending on the version of node you are using you'll need to utilize Promise with .then and .catch or the more current async and await, your issue is JS operates asynchronously so the call to your endpoint is happening the same time your function is looking for the data to resolve the GetNewFact intent. there is lots of good documentation on how to implement this to get your function working.Chuck LaPress

1 Answers

3
votes

Node.js is asynchronous by default, which means that your response builder is called before the data is returned by the service. I've had similar issue while calling the customer profile api to get phone number, I solved it using async-await like this

async handle(handlerInput) {
    const { serviceClientFactory, responseBuilder } = handlerInput;
    try {
      const upsServiceClient = serviceClientFactory.getUpsServiceClient();
      const profileMobileObject = await upsServiceClient.getProfileMobileNumber();
     
      const profileMobile = profileMobileObject.phoneNumber;
      const speechResponse = `Hello your mobile number is, ${profileMobile}</say-as>`;
      const cardResponse = `Hello your mobile number is, ${profileMobile}`
      return responseBuilder
                      .speak(speechResponse)
                      .withSimpleCard(APP_NAME, cardResponse)
                      .getResponse();
    } catch (error) {
      console.log(JSON.stringify(error));
      if (error.statusCode == 403) {
        return responseBuilder
        .speak(messages.NOTIFY_MISSING_PERMISSIONS)
        .withAskForPermissionsConsentCard([MOBILE_PERMISSION])
        .getResponse();
      }
      console.log(JSON.stringify(error));
      const response = responseBuilder.speak(messages.ERROR).getResponse();
      return response;
    }
  },

Make the function async and use await before you call the service.. You can also do this by Promises