Search code examples
node.jsalexa-skills-kit

how to make sure my node.js handler finish an api call before it returns a value?


I want to make sure my handler returns "speakoutput" after it receives the result from IBM Watson API. When my code calls IBM API, it will directly jump to "return handlerInput.responseBuilder", because it takes some time for IBM API to analyze the input text.

I tried "await", "promise", but it was not working for my case. "Await" and "promise" could make sure I receive the result from API, but it never prevent my code from jumping to the next line before it finish the API call.

How do I solve this problem?

const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    var speakoutput ='';


//IBM API HERE
    var NaturalLanguageUnderstandingV1 = require('watson-developer-cloud/natural-language-understanding/v1.js');
    var nlu = new NaturalLanguageUnderstandingV1({
      iam_apikey: 'my_api_key',
      version: '2018-04-05',
      url: 'https://gateway.watsonplatform.net/natural-language-    understanding/api/'
    });


//nlu.analyze takes a lot of time to process
    nlu.analyze(
      {
        html: 'Leonardo DiCaprio won Best Actor in a Leading Role for his performance', // Buffer or String
        features: {
          //concepts: {},
          'keywords': {},
          'relations': {},
          'sentiment': {
            'targets': [
              'in'
            ]
          }
        }
      },
      function(err, response) {
        if (err) {
          console.log('error:', err);
        } else {
          //console.log(JSON.stringify(response, null, 2));
          var temparray = [];
          for (i in response.keywords){
            speakoutput +=response.keywords[i].text;
            console.log(JSON.stringify(response.keywords[i].text, null, 2));
            temparray.push(response.keywords[i].text);
          }
          console.log(temparray);
        }
      }
    );


//my code will jump to this part before it finishes "nlu.analyze"
    return handlerInput.responseBuilder
      .speak(speakoutput)
      .reprompt('What do you want to know? you could search data for atm, course search, fed events,')
      .getResponse();
  },
};

Solution

  • Convert the cb to a promise and return the promise chain as your handle function. Whatever calls handle must also use .then() or await

    Example of an aync handle() can be found at https://github.com/alexa/skill-sample-nodejs-city-guide/blob/master/lambda/custom/index.js

    const GoOutHandler = {
        canHandle(handlerInput) {
            const request = handlerInput.requestEnvelope.request;
    
            return request.type === 'IntentRequest' && request.intent.name === 'GoOutIntent';
        },
        handle(handlerInput) {
            return new Promise((resolve) => {
                getWeather((localTime, currentTemp, currentCondition) => {
                    const speechOutput = `It is ${localTime
                    } and the weather in ${data.city
                    } is ${
                        currentTemp} and ${currentCondition}`;
                    resolve(handlerInput.responseBuilder.speak(speechOutput).getResponse());
                });
            });
        },
    };
    

    So for yours:

    const LaunchRequestHandler = {
      canHandle(handlerInput) {
        return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
      },
      handle(handlerInput) {
    
    
    
    //nlu.analyze takes a lot of time to process
        return (new Promise(function(resolve,reject){ // convert cb to promise
    
            var speakoutput ='';
    
    
        //IBM API HERE
            var NaturalLanguageUnderstandingV1 = require('watson-developer-cloud/natural-language-understanding/v1.js');
            var nlu = new NaturalLanguageUnderstandingV1({
              iam_apikey: 'my_api_key',
              version: '2018-04-05',
              url: 'https://gateway.watsonplatform.net/natural-language-    understanding/api/'
            });
    
            nlu.analyze(
            {
                html: 'Leonardo DiCaprio won Best Actor in a Leading Role for his performance', // Buffer or String
                features: {
                    //concepts: {},
                    'keywords': {},
                    'relations': {},
                    'sentiment': {
                        'targets': [
                        'in'
                        ]
                    }
                }
            },
            function(err, response) {
                if (err) {
                    reject(err);
                } else {
                    //console.log(JSON.stringify(response, null, 2));
                    var temparray = [];
                    for (i in response.keywords){
                        speakoutput +=response.keywords[i].text;
                        console.log(JSON.stringify(response.keywords[i].text, null, 2));
                        temparray.push(response.keywords[i].text);
                    }
                    console.log(temparray);
                    resolve(handlerInput.responseBuilder
                .speak(speakoutput)
                .reprompt('What do you want to know? you could search data for atm, course search, fed events,')
                .getResponse());
                }
            }
            );
        }))
      },
    };