1

I had managed to call these 2 methods (geocoder.reverse, timezone.data) one after the other successfully in V1 DialogflowApp, but I am now upgrading to V2, and can't seem to get both asynchronous method calls right. Please help. I use these packages respectively

  • node-geocoder: ^3.22.0
  • node-google-timezone: ^0.1.1

         const options = { provider: 'google', httpAdapter: 'https', apiKey: googleApi, formatter: 'json'};
         const geocoder = NodeGeocoder(options);
         const timezone = require('node-google-timezone');
    
    app.intent('saving_prompt', (conv) => {
            if (conv.data.area) {       
               const deviceCoordinatesStart = conv.device.location.coordinates;
               const latitudeValueStart = deviceCoordinatesStart.latitude;
               const longitudeValueStart = deviceCoordinatesStart.longitude;
               let start = moment(); //Start time in UTC, you could also use dateTime()
               let startTz = momentTz(); //used because of getting the Timezone
               const timestamp = 1402629305; // Just a dud placeholder to fulfil timezone function
    
        //GEOLOCATION REVERSAL (METHOD 1)
        geocoder.reverse({lat:latitudeValueStart, lon:longitudeValueStart}, (err, res) => {
            if (err) {
                console.log(err);
            }
            let startLocation = res[0].administrativeLevels.level1long; 
            conv.data.startLocation = startLocation;
    
                //GETTING LOCAL TIMEZONE FROM LAT/LONG AND TIMESTAMP (METHOD 2)
                timezone.data(latitudeValueStart, longitudeValueStart, timestamp, (err, tz) => { 
                    if (err) {
                        console.log(err);
                        }
                    var zoneHolder = tz.raw_response.timeZoneId;  
                    const localTime = startTz.tz(zoneHolder).format('LLL'); 
                    conv.data.localTime = localTime; 
                    app.ask('Your race starts by ' + localTime + 'at' + startLocation);
                    conv.ask(new Suggestions('End Race'));
                });
        });
    } else {  //Returned if permission not granted
            conv.ask('You do not have your location on. Say "Locate me", to retrieve your location');
            conv.ask(new Suggestions(['Locate me', 'Exit']));
    
    }
    });
    

//FUNCTION CREATED TO GET TIMEZONE WITHOUT 'node-google-timezone' PACKAGE

function callTimezoneApi(lat, lon) {
    return new Promise((resolve, reject) => {
         let path = '/maps/api/timezone/json?location=' + lat +
          ',' + lon + '&timestamp=' + ts + '&key=' + googleApi;
         console.log('API Request: ' + host + path);
         // Make the HTTP request to get the weather
         http.get({
          host: host,
          path: path
         }, (res) => {
              let body = ''; // var to store the response chunks
              res.on('data', (d) => {
               body += d;
              }); // store each response chunk
              res.on('end', () => {
                   let response = JSON.parse(body);
                   let zoneHolder = response.timeZoneId;
                   let startTz = momentTz(); //used in fulfilling the Timezone
                   const localTime = startTz.tz(zoneHolder).format('LLL');
                   console.log(localTime);
                   return resolve(localTime);
              });
          res.on('error', (error) => {reject(error);});
        });
    });
}
Ade
  • 363
  • 3
  • 13

1 Answers1

2

If you use Node 8 you can use async/await to untangle the nested callback structure. It will ensure that your asynchronous calls are completed before proceeding with the next call, that has dependency on another async value.

app.intent('saving_prompt', async (conv) => {
    if (conv.data.area) {
        const deviceCoordinatesStart = conv.device.location.coordinates;
        const latitudeValueStart = deviceCoordinatesStart.latitude;
        const longitudeValueStart = deviceCoordinatesStart.longitude;
        let start = moment(); //Start time in UTC, you could also use dateTime()
        let startTz = momentTz(); //used because of getting the Timezone
        const timestamp = 1402629305; // Just a dud placeholder to fulfil timezone function

        const startLocation = await new Promise((resolve, reject) => {
            geocoder.reverse({ lat: latitudeValueStart, lon: longitudeValueStart }, (err, res) => {
                if (err) {
                    console.log(err);
                    reject(err);
                } else {
                    resolve(res[0].administrativeLevels.level1long);
                }
            });
        });

        const localTime = await new Promise((resolve, reject) => {
            timezone.data(latitudeValueStart, longitudeValueStart, timestamp, (err, tz) => {
                if (err) {
                    console.log(err);
                    reject(err);
                } else {
                    var zoneHolder = tz.raw_response.timeZoneId;
                    resolve(startTz.tz(zoneHolder).format('LLL'));
                }
            });
        });

        app.ask('Your race starts by ' + localTime + 'at' + startLocation);
        conv.ask(new Suggestions('End Race'));
    } else {  //Returned if permission not granted
        conv.ask('You do not have your location on. Say "Locate me", to retrieve your location');
        conv.ask(new Suggestions(['Locate me', 'Exit']));

    }
});
Dennis Alund
  • 2,916
  • 1
  • 13
  • 34
  • Thank you very much for the response, unfortunately Firebase functions - which is the runtime I use, is predicated on node 6, which does not support await/async. Else it would've been an easy wrap. – Ade Dec 13 '18 at 11:11
  • You can migrate it to node 8: https://cloud.google.com/functions/docs/concepts/nodejs-8-runtime – Dennis Alund Dec 13 '18 at 15:37
  • But I believe that migration is only applicable to cloud functions, and not firebase functions which Dialogflow V2 uses. When I had used similar code in Dialogflow V1 - then running on cloud functions, it had worked hitchlessly. – Ade Dec 14 '18 at 09:22
  • Cloud functions for firebase are the very same cloud functions. – Dennis Alund Dec 14 '18 at 09:28
  • I had thought so until I found Firebase functions to be Node 6(without support for async await), and cloud functions to be Node 8, maybe I am mistaken? https://developers.google.com/actions/reference/nodejs/lib-v1-migration – Ade Dec 17 '18 at 17:47
  • 1
    https://firebase.googleblog.com/2018/08/cloud-functions-for-firebase-config-node-8-timeout-memory-region.html?m=1 – Dennis Alund Dec 18 '18 at 07:18