Search code examples
javascriptcallbackjsonptwitch

What is the scope of the callback on my vanilla JSONP


A little backstory, I'm using the Twitch (game streaming service) api to pull a list of followers from a channel on the site. In order to get around the CORS rules I'm using JSONP. Personally, I prefer to use vanilla javascript over libraries so I learned how to do it from the excellent article over on https://cameronspear.com/blog/exactly-what-is-jsonp/ .

Below is my simplified javascript (the actual code probably isn't extremely necessary, but maybe it'll help to clarify my question afterward):

//Function to pull JSONP

function pullJSONP(url) {

    var JSONPInstance = document.createElement('script');
    JSONPInstance.src = url;

    JSONPInstance.onload = function () {
        this.remove();
    };

    var head = document.getElementsByTagName('head')[0];
    head.insertBefore(JSONPInstance, null);
} 


//End function to pull JSONP


function storeCurrentFollows(offset) //Function to wrap entire operation
{


    function pullStartSessionFollows(JSONdata) //Callback from JSONP down below
    { 


        function pushNameElements(followEntry) //Function to add names into array
        {

            allSessionFollows.push(followEntry.user.name);

        }

        JSONdata.follows.forEach(pushNameElements); //Iterate through followers

        storeCurrentFollows(offset+100); //Rerun operation, incrementing offset by 100

    }; 

    if(offset > 300){ return }
    else
    {
        //running JSONP function from above, passing in url and setting a callback
        pullJSONP('https://api.twitch.tv/kraken/channels/' + channelName + '/follows?direction=desc&limit=100&offset=' + offset + '&callback=pullStartSessionFollows');

    }


}


storeCurrentFollows(0);

So my question is this, whenever I run this operation with the items in this order it returns the error > Uncaught ReferenceError: pullStartSessionFollows is not defined in the console. It doesn't work properly unless I move the pullStartSessionFollows function up into the global scope. What I don't understand is why it thinks it is undefined even though it is instantiated just before I run the pullJSONP() function. I don't want to move it because then I'll have to pass in my offset to two different functions as well as a few other issues. Any help or insight would be very much appreciated. Thank you in advance!


Solution

  • It doesn't work properly unless I move the pullStartSessionFollows function up into the global scope.

    Correct, JSONP callbacks must be global functions. It's intrinsic to the way JSONP works.

    What I don't understand is why it thinks it is undefined even though it is instantiated just before I run the pullJSONP() function.

    Because that creates the function within the function where you do that (storeCurrentFollows), not as a global. But the way JSONP works requires that the function be global.

    You can have the pullStartSessionFollows function only exist while you have a pending JSONP call, by declaring it as a global variable:

    var pullStartSessionFollows;
    

    ...and then assigning to that within storeCurrentFollows:

    pullStartSessionFollows = function() { /* ... */ };
    

    You can also remove it when the callback has done its work:

    pullStartSessionFollows = undefined;
    

    Why must the callback function be a global? Because the way JSONP works is by adding a script element to the page, as though you had written this in the HTML:

    <script src="http://example.com/get?callback=pullStartSessionFollows"></script>
    

    ...and then the response looks like:

    pullStartSessionFollows({/*...JSON here...*/})
    

    That requires that it be a global function.