I have an event handler which is making an external call to a REST interface. The event handler is setup to perform the this.emit(':ask', ...) in the callback from a request call to the remote service. The service takes about 1-2 seconds to return data and is asynchronous (from the npm request package)
var request = require('request);
function myHandler(input, callback) {
request('http://rest_url...', function(error, response, body) {
if (response) { // we have a success so call the call back
callback(null, 'found it!');
} else {
callback(error);
}
}
}
The callback, which is called from the event handler, then performs the this.emit(':ask'),
'myEventHandler': function() {
myHandler('findThisData', (error, rsp) => {
if(error) {
this.emit(":tell", "I'm sorry, but I can't find that information.");
} else {
this.emit(":ask", " I found!", "Want to do anything else?");
});
// notice no emit or context.success/fail/done at end of event handler
}
The alexa-sdk seems to call HandleLambdaEvent() which calls EmitEvent which will return from myEventHandler before the callback is executed. The delay return in the asynchronous callback will get executed after HandleLambdaEvent() has returned. This sequence seems to return a 'null' to the lambda function and fails to ever get a chance to handle the eventual call to this.emit() processed in the callback function.
Have you tried creating a variable outside of the function?
Ex. var self = this;
self.tell(':tell','found it!');
I did not explicitly create a new variable, but I did verify that the correct _this_ was bound at the execution. Since the fat arrow is used, _this_ should be bound correctly. Over the weekend, I may see if an explicit Promise resolves the issue. I was able to work around it by using the request_sync library.
I have the same issue. Were you able to resolve it?
The keyword this is bounded to the last function scope, which is in your case the request callback block.
Instead of writing function (error, response, body) { .. } you should write (error, response, body) => { ... }. This does not re-bind the this-scope, so that this is not changed in the callback.
Hope this helps you @svonkleeck and @JasonVonKrueger.
Complete fix for the original issue on top:
var request = require('request);
function myHandler(input, callback) {
request('http://rest_url...', (error, response, body) => {
if (response) { // we have a success so call the call back
callback(null, 'found it!');
} else {
callback(error);
}
}
}
Thanks @jerolimov - this will work just fine
If arrow functions aren't your thing, another popular option is to keep a pointer to the real 'this' context at the top of the function. I've made it a practice in cases where I have callback functions in case I forget to use an arrow function.
function myHandler(input, callback) {
const self = this; // <--- pointer to this in the outer function scope
request('http://rest_url...', function(error, response, body) {
if (response) { // we have a success so call the call back
self.emit(":tell", "hello"); // <--- self points to the object you want
} else {
self.emit(':ask', 'hello');
}
}
}
closing this as resolved. Feel free to re-open if you have further questions
I have been trying to call the ticketmaster API in an alexa skill I am building using the SDK and am having a lot of trouble accessing the JSON response. I tried to use the format that you have outlined, but I clearly do not know how to access the right part of the response. In my function, I am doing self.emit and emitting the list of Object keys for "response", which does not seem to be the JSON response. Body, I have discovered, is of type string, also not the JSON response. I would appreciate any advice you could give me. @bclement-amazon @jerolimov
'showsNearMe': function (input,callback) {
var filledSlots = delegateSlotCollection.call(this);
//compose speechOutput that simply reads all the collected slot values
var speechOutput = randomPhrase(showIntro);
var showCity=this.event.request.intent.slots.showCity.value;
var timePeriod=this.event.request.intent.slots.timePeriod.value + 'T10:00:00Z';
const self = this; // <--- pointer to this in the outer function scope
request("https://app.ticketmaster.com/discovery/v2/events.json?city=Boston&size=1&segmentName=Music&apikey=bP5gQH4GDftg8tE8j3dyXHYYPdNzDBV7", function(error, response, body) {
if (response) {
self.emit(":tell",Object.keys(response).join(' '));
} else {
self.emit(':ask', 'hello');
}
})
},
Hello together,
I have also the problem the when asking Alexa for something, there is always this NoSpeechlet response when invoking the searchIntent. I have been struggling for two days without a solution. Here is the code:
'use strict';
var Alexa = require('alexa-sdk');
var http = require('http');
var axios = require('axios');
var calendar = require('./helpers/googleCalendar.js');
var self = this;
// Register handlers and initialize start
exports.handler = function(event, context, callback) {
var alexa = Alexa.handler(event, context);
alexa.appId = APP_ID;
alexa.registerHandlers(newSessionHandlers, startSearchHandlers);
console.log('before exec');
alexa.execute();
console.log('after exec');
};
// Reference to Alexa object
// States
const states = {
SEARCHMODE: '_SEARCHMODE'
};
// Messages and other reoccuring data
const APP_ID = 'amzn1.ask.skill.e0cd726d-f53c-4769-967c-8cacd8862a80';
const helpMessage =
'For example, you can ask: What are my appointments for today?';
const HELP_REPROMPT = 'What can I help you with?';
const STOP_MESSAGE = 'Goodbye!';
const SKILL_NAME = 'Miracle';
const welcomeMessage = 'You can ask for your todays appointments';
const shutdownMessage = 'Ok. See you soon.';
const errorMessage = 'Ups... Something went wrong. Please start me again.';
// Adding session handlers
const newSessionHandlers = {
NewSession: function() {
this.handler.state = states.SEARCHMODE;
console.log('I am in the launch');
this.response
.speak(`Welcome to ${SKILL_NAME}` + ' ' + ' ' + welcomeMessage)
.listen(welcomeMessage);
this.emit(':responseReady');
},
Unhandled: function() {
this.response.speak(helpMessage).listen(helpMessage);
this.emit(':responseReady');
}
};
// Creating hanlders for state SEARCH state
const startSearchHandlers = Alexa.CreateStateHandler(states.SEARCHMODE, {
// Built-in intents
'AMAZON.YesIntent': function() {
output = welcomeMessage;
this.response.speak(output).listen(welcomeMessage);
this.emit(':responseReady');
},
'AMAZON.NoIntent': function() {
this.response.speak(shutdownMessage);
this.emit(':responseReady');
},
// 'AMAZON.RepeatIntent': function() {
// this.response.speak(output).listen(HelpMessage);
// },
SessionEndedRequest: function() {
this.emit('AMAZON.StopIntent');
},
searchIntent: function() {
const url = 'http://127.0.0.1:5000/customers';
axios.get(url).then(
response => {
console.log('Success!', response); // Yea, REST all the things
self.emit(':tell', 'ok');
},
error => {
console.error('Failure!', response); // Boo, bad feels
self.emit(
':tell',
"Hmm.. that didn't work. Check the CloudWatch Luke."
);
}
);
}
});
I really appreciate your help.
Best regards
Oli
Most helpful comment
Thanks @jerolimov - this will work just fine
If arrow functions aren't your thing, another popular option is to keep a pointer to the real 'this' context at the top of the function. I've made it a practice in cases where I have callback functions in case I forget to use an arrow function.
closing this as resolved. Feel free to re-open if you have further questions