Anders H
Anders H

Reputation: 651

JSON Output remains undefined within code but alert is fine, is this an issue with scope?

I'm using the Facebook graph API to get a user's friend list and generate a status message based on a template and random friends. If I use alert(response.data[0].name); instead of return(response); my code works and I see the name of the first Facebook friend. When I take it further in my code, it throws an error as undefined. Based on a few hours of trying to work out the problem, I'm thinking I have some kind of issue with scope that doesn't allow me to use the response any longer or a problem with JSON formatting. I tried playing around with jQuery's JSON parse as well but didn't gain anything additional there.

shuffle = function(v){
    for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
    return v;
};

function getFriends() {
    FB.login(function(response) {
        if (response.session && response.perms) {
        FB.api('/me/friends', function(response) {
            return response;
        });
        }
    } , {perms:'publish_stream'});
};

function selectFriend(){
    var friends = getFriends();
    friends = friends.data[0];
    var findex = friends.length
    var randomNumber = (Math.floor( Math.random() * 10 ) % findex);
    return(friends[randomNumber].name);
};

function process(status){
    status = status[0].toString();
    var cindex = status.indexOf("{{friend}}");

    while (cindex != -1){
        status = status.replace("{{friend}}",selectFriend());
        cindex = status.indexOf("{{friend}}");
    }
    return(status);
};

function newStatus(){
    var status = [
        "This status is named {{friend}}",
        "This status loves {{friend}} but likes {{friend}} too.",
        "This status goes by {{friend}} but you can call it {{friend}} if you like.",
        "This status hates {{friend}}"
    ];

    status = shuffle(status);
    $('#status').text(process(status));
}

The code begins with calling newStatus() on click.

EDIT: Here's how I refactored my code to work after jfriend00's answer:

shuffle = function(v){
    for(var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x);
    return v;
};

function newStatus(){
    var status = [
        "This status is named {{friend}}",
        "This status loves {{friend}} but likes {{friend}} too.",
        "This status goes by {{friend}} but you can call it {{friend}} if you like.",
        "This status hates {{friend}}"
    ];
    status = shuffle(status);
    status = status[0].toString();

    FB.login(function(response) {
        if (response.session && response.perms) {
        FB.api('/me/friends', function(response) {
            var friends = response.data;
            var findex = friends.length

            var cindex = status.indexOf("{{friend}}");

            while (cindex != -1){
                var randomNumber = (Math.floor( Math.random() * 10 ) % findex);
                var name = friends[randomNumber].name;

                status = status.replace("{{friend}}",name);
                cindex = status.indexOf("{{friend}}");
            }
            $('#status').text(status);
        });
        }
    } , {perms:'publish_stream'});
}

Upvotes: 1

Views: 301

Answers (1)

jfriend00
jfriend00

Reputation: 707736

The issue is probably that the calls to the FB api are asychronous. That means when you call FB.api(), it starts an ajax call to FB. Your other code continues to run while that ajax call is being completed. In fact, your call to getFriends finishes and the rest of selectFriend() runs too before the FB ajax call has completed.

Then, when the FB api call does complete, it calls the completion function (probably where you put an alert). In your code, you aren't doing anything meaningful in the completion function. You're just returning the response with

return response;

But, that isn't doing anything useful. That return value is not a return from the getFriends() function. It's a return back to something inside the ajax engine.

If you want to do something useful with the FB.api() response, you need to run that code or call that code from the completion function specified in the FB.api() call.

You would have to do something like this pseudo code:

function getFriends() {
    FB.login(function(response) {
        if (response.session && response.perms) {
        FB.api('/me/friends', function(response) {
            var friends = response.data[0];
            var findex = friends.length
            var randomNumber = (Math.floor( Math.random() * 10 ) % findex);
            var name = friends[randomNumber].name;
            // now continue on with whatever you want to do with the random friend info
            // you cannot just return data from this callback, you have to actually carry
            // out whatever you were going to do with the data here
        });
        }
    } , {perms:'publish_stream'});
};

function selectFriend(){
    getFriends();
    // the getFriends() function is asynchronous and the FB.api() call has not yet
    // completed so we can't do anything here yet as we have to wait for the
    // FB.api() call to finish first.
};

Upvotes: 2

Related Questions