0

I've been doing some work with callbacks in Node.js, working on a messenger bot.

I wrote a function to pull out the user's first name and pass it to another function. I needed to add a callback to the function to get it to work. My function for getting the user name looks like this:

function getFBUserName(callback)
{
    let graphURL = 'https://graph.facebook.com/v2.6/';
    let graphFields = '?fields=first_name,last_name,gender';
    let graphToken = '&access_token=';
    let reqURL = graphURL + usrPSID + graphFields + graphToken + config.FB_PAGE_ACCESS_TOKEN;
    let reqJSON;
    request(reqURL, function (error, response, body) {
        console.log('error:', error); // Print the error if one occurred
        console.log('statusCode:', response && response.statusCode); // Print the response 
        // status code if a response was received
        console.log(body);
        reqJSON = JSON.parse(body);
        if (reqJSON.hasOwnProperty("first_name")){
        console.log("First Name exists"); // Debugging, to see if hasOwnProperty is working
            let userName = reqJSON['first_name'];
            callback(userName);
        }
    });
}

This function then passes a callback to my handlePostback() function:

function handlePostback(sender_psid, received_postback) {
    let response;
    let responseText;

    // Get the payload for the postback
    let payload = received_postback.payload;

    // Set the response based on the postback payload
    if (payload === 'GET_STARTED') {
        let userName;
        getFBUserName(function (uName) {
            userName = uName;
        responseText = "Hi there " + userName + ", welcome to the bot!";
        response = {"text": responseText};
        callSendAPI(sender_psid, response);
        });
    }
}

The code above works. But if I change my code so it looks like the following, it DOESN'T work. The userName variable is ALWAYS undefined.

function handlePostback(sender_psid, received_postback) {
    let response;
    let responseText;

    // Get the payload for the postback
    let payload = received_postback.payload;

    // Set the response based on the postback payload
    if (payload === 'GET_STARTED') {
        let userName;
        getFBUserName(function (uName) {
            userName = uName;
        });
        responseText = "Hi there " + userName + ", welcome to the bot!";
    }
    response = {"text": responseText};
    callSendAPI(sender_psid, response);
}

I want to use the second code structure, because as I add postbacks to my if statement there's going to be a lot of repetition of code. It would make sense to set up the responseText for the postback within the if statement, then set the response and send the message outside the loop. So my question is, why does the FIRST structure work, but the SECOND does not?

  • I wonder if there are stats somewhere about the most duplicated questions... This one must be asked about every hour or so... – jcaron Nov 28 '17 at 13:42

1 Answers1

0

The problem is that getFBUserName is an asynchronous function that executes a callback when it's done, so the lines below the getFBUseName call, are probably executed before the callback.

Try executing this piece of code and you may understand better what are callbacks.

setTimeout(function () {
  console.log('Callback function')
}, 500)
console.log('This is called first!')

The callback function is passed to setTimeout, setTimeout(callback, timeInMillisecons), so it is executed after that time, and the other console.log is executed before.

This is a silly example but the same goes on when you are making a request to an API (like Facebook API). You don't know how long the request will take to respond, but you want to execute some code after that, and using the data you fetch. So you can use a callback.

Facundo Matteo
  • 2,347
  • 2
  • 16
  • 19