Dialogflow-fulfillment-nodejs: No response from Callback methods, call ends before callback completes

Created on 6 Jul 2018  路  9Comments  路  Source: dialogflow/dialogflow-fulfillment-nodejs

I am using this Node client with express as in #81 . I have integrated a temperature service to my webhook. But the problem is from the callback method, I am not able to send any response back to user.

function getTemp (agent) {
    console.info(`temp ${(agent.getContext('param_context').parameters)['param1']}`);
    agent.add(`Temperature is 50 degree Celsius!`); // This works
    ...
    ...
    serviceRequest.getTemperature(city, function(resp){
       let temp = resp['temp'];
       agent.add(`Temperature is ${temp}`) ; // This doesnot work !!!  THROWS ERROR **
    });
}

What is the root cause of the issue?
How can I resolve it as no error is coming!!

Most helpful comment

As mentioned in the package, sync-request is not suitable for production and can cause your systems to freeze and be unstable. I'd recommend returning a promise in your handler as I described in my last comment for performance reasons.

All 9 comments

I have similar issue with promises while working with firebase database.

function singUp() {
    db.ref('users')
        .set(userData)
        .then(() => {
          agent.add('account created'); // don't work
        })
        .catch((error) => {
          agent.add('some error', error.message); // don't work
        });
    agent.add('account created'); // this works fine
}

I ran some tests and it looks timeout is not an issue.
The webhook method call ends up before completing the callback process.

Here are the code and captured console logs:

Temperature code

function getTemp(agent) {
    const timeStart = new Date().getTime();
    console.info(`temp function called`);
    // agent.add(`Temperature in New Delhi is 50 degree Celsius!`); // This works
    serviceRequest.getTemp("New Delhi", function(resp){
        if (resp['status'] === 'Y') {
            // success
            const msg = `Temperature in New Delhi is ${resp['temp']} Celsius!`;
            console.log(`Speech Response -- ${msg}`);
            console.log(`Type of msg -> ${typeof msg}`);
            agent.add(msg);
        } else {
            // failure
            agent.add(`There was some error with the backend. Please try again later.`);
        }
        const timeEnds = new Date().getTime();
        console.log("\nComplete 'getTemp' process took " + (timeEnds - timeStart) + " milliseconds.")
    });
    console.log("------ temperature function ends here ------");
}

getTemp function in service request file

'getTemp': function (city, callback) {
        let respBack = {};
        doTimeOutTest(function(resp){
            respBack['temp'] = resp;
            respBack['status'] = 'Y';
            callback(respBack);
        });
 }

doTimeOut function

function doTimeOutTest(callback){
    setTimeout(function() {
        callback("30 degree");
    }, 1);
    // for(let i=0; i<10000; i++){
    //     for(let j=0; j<10000; j++){
    //         //
    //     }
    // }
    // callback("30 degree");
}

Console Logs

>>>>>>> S E R V E R   H I T <<<<<<<

temp function called
------ temperature function ends here ------
Speech Response -- Temperature in New Delhi is 30 degree Celsius!
Type of msg -> string

Complete 'getTemp' process took 10 milliseconds.

RESULT on DIALOGFLOW

!! NO RESPONSE !!

doTimeOut function

function doTimeOutTest(callback){
    // setTimeout(function() {
    //     callback("30 degree");
    // }, 1);
    for(let i=0; i<10000; i++){
        for(let j=0; j<10000; j++){
            //
        }
    }
    callback("30 degree");
}

Console Logs

>>>>>>> S E R V E R   H I T <<<<<<<

temp function called
Speech Response -- Temperature in New Delhi is 30 degree Celsius!
Type of msg -> string

Complete 'getTemp' process took 77 milliseconds.
------ temperature function ends here ------

RESULT on DIALOGFLOW

Temperature in New Delhi is 30 degree Celsius!

Findings

  1. The first one took less time but the function ended before the callback was processed. "------ temperature function ends here ------" is printed before the callback logs.

  2. The second one took more time but the function worked as expected and "------ temperature function ends here ------" is printed after the callback logs and the end. This time response was seen on the dialogflow.

What is the root cause of such behaviour, why the function is getting ended before callback is executed?

StackOverflow question for this issue.

function handlers use promises so you can return a promise and process things like http requests in the promise. Here is an example of using the request library:

function dialogflowHanlderWithRequest(agent) {
  return new Promise((resolve, reject) => {
    request.get(options, (error, response, body) => {
      JSON.parse(body)
      // processing code
      agent.add(...)
      resolve();
    });
  });
};

You can also move the HTTP call to another function that returns a promise. Here is an example with the axios library:

function dialogflowHandlerWithAxios(agent) {
  return callApi('www.google.com').then(response => {
    agent.add('My response');
  }).catch (error => {
    // do something
  })
};

function callApi(url) {
    return axios.get(url);
}

See issue 3 for more context

Using Sync-Request also worked for me. Is that a correct approach?

As mentioned in the package, sync-request is not suitable for production and can cause your systems to freeze and be unstable. I'd recommend returning a promise in your handler as I described in my last comment for performance reasons.

That works, thanks @matthewayne !

Yes @matthewayne sync-request is a blocking process. I am passing promise as you stated above :)

Is it possible to nest the requests? I mean if we want to use response of first request to fetch other data?

function dialogflowHandlerWithAxios(agent) {
  return callApi('www.google.com').then(response => {
    secondCallApi(response.url).then(response => {
    agent.add('My response');
    }).catch (error => {
        // do something
    })
  }).catch (error => {
    // do something
  })
};

function callApi(url) {
    return axios.get(url);
}

function secondCallApi(url) {
    return axios.get(url);
}
Was this page helpful?
0 / 5 - 0 ratings