Search code examples
alexa-skills-kitalexa-sdk-nodejs

How to use async functions within Alexa skill properly?


I am very new to async programming, so forgive me for the lack of understanding, but I am currently building an Alexa skill which calls a private parking API. You can call this API and it will give you the nearest parking spots.

    const getParkingSpots_Handler =  {
        canHandle(handlerInput) {
            const request = handlerInput.requestEnvelope.request;
            return request.type === 'IntentRequest' && request.intent.name === 'getParkingSpots' ;
        },
        handle(handlerInput) {
            const request = handlerInput.requestEnvelope.request;
            const responseBuilder = handlerInput.responseBuilder;
            let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();

            let requestData = {
                // I can't show this sorry
                }

            let options = {
               // I can't show this sorry
            };

            // Call to the API
            const postAxios = async () => {
                try {
                    const response = await axios.post(API_URL, requestData, options);
                    return response.data.result;
                } catch(error) {
                    console.log(error);
                }
            };

            // Another function. This is where I use the data from the API response. I intent to add some code here that only picks out a number of results, sorts it by price etc. etc.
            const useTheResult = async () => {
                const result  = await postAxios();
                console.log('Response from the API:', result);
            };

            // We defined the functions above, now we need to execute them
            useTheResult();

            // This is what we will refer to the 'problem code'.
            let say = `Hello from confidientialCompany! You can park...`;
                return responseBuilder
                    .speak(say)
                    .reprompt('try again, ' + say)
                    .getResponse();
        },
    };

Ideally once I add the code to modify the response within useTheResult, I want the problem code to be inside useTheResult as well...why? Because once I've picked out the data I want and modified it, I'll try to turn say into an 'Alexa-readable' sentence like:

    let say = `Hello from confidentialCompany! You can park on ${roadName1}, ${roadName2} and ${roadName3}. Prices start from ${startingPrice} pounds.`

If I do that right now, as it is, I get an error when testing it out in the Alexa console. I have no idea what to do anymore, and I feel like I'm going to get stuck in an infinite loop of async functions.


Solution

  • Add async keyword to handle method name and use await inside:

    const getParkingSpots_Handler =  {
            canHandle(handlerInput) {
                const request = handlerInput.requestEnvelope.request;
                return request.type === 'IntentRequest' && request.intent.name === 'getParkingSpots' ;
            },
            async handle(handlerInput) {
                const request = handlerInput.requestEnvelope.request;
                const responseBuilder = handlerInput.responseBuilder;
                let sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
    
                let requestData = {
                    // I can't show this sorry
                    }
    
                let options = {
                   // I can't show this sorry
                };
    
                // Call to the API
                 let result = null;
                 try {
                     const response = await axios.post(API_URL, requestData, options);
                     result = response.data.result;
                 } catch(error) {
                     // handle this case and return some message to User
                     console.log(error);
                 }
                
                // assume your data structure to be like:
                /**
                 result: {
                   roadName1: "1st street",
                   roadName2: "2nd street",
                   roadName3: "3rd street", 
                   startingPrice: "1.2"
                 }
                */
                const {roadName1, roadName2, roadName3, startingPrice} = result;
    
                // This is what we will refer to the 'problem code'.
                let say = `Hello from confidentialCompany! You can park on ${roadName1}, ${roadName2} and ${roadName3}. Prices start from ${startingPrice} pounds.`;
                    return responseBuilder
                        .speak(say)
                        .reprompt('try again, ' + say)
                        .getResponse();
            },
        };
    

    If you want to do more calls in the same function:

    try {
       const [response1, response2] = await Promise.all([
          axios.post(API_URL1, requestData, options),
          axios.post(API_URL2, requestData, options)
       ]);
       // do things with your responses
       // ...
    } catch(error) {
       // handle this case and return some message to User
       console.log(error);
    }