简体   繁体   中英

synchronicity problems with Internet Explorer

I'm developing a javascript widget using the UWA widget format. Unfortunately this makes it impossible to jsFiddle my code but I've commented it in detail so that, hopefully, you can follow its fairly straightforward sequence.

HighestClass = {};
HighestClass.array = [];
HighestClass.url = "http://our.url.local/frog/pointsByWeek.php?cmd=highestClass&students=";

HighestClass.init = function(groupPrefix) {
    var count = 0;

    /* Using the group prefix, i.e. "CLS 9", from the drop-down box,
        get a list of all of the classes in that year group */

    /* First time round, count the number of groups that match this
        syntax because there are no parameters available to filter
        this API */

    Frog.API.get('groups.getAll',{
        'onSuccess': function(data){
        for (var i = 0; i < data.length; i++) {
            if (data[i].name.indexOf(groupPrefix) != -1)
                count++;
        }
    });

    /* Now that these classes have been counted, run through the API
        call again to push each class ID through to another function */

    var run_through = 0;

    Frog.API.get('groups.getAll',{
        'onSuccess': function(data){
        for (var i = 0; i < data.length; i++) {
            if (data[i].name.indexOf(groupPrefix) != -1) {
                var end = false;

                run_through++;

                /* When it gets to the last class group, i.e. the run_through 
                    variable becomes equal to count, let the getClassPoints
                    function know */

                if( run_through == count )
                    end = true;

                HighestClass.getClassPoints( data[i].name, data[i].id, end );
            }   
        }
        }
    });
}

HighestClass.getClassPoints = function(name, id, end) {
    var list = '';

    /* Using the ID of the class group, create a comma-separated list
        of students for use in our MySQL query */

    Frog.API.get("users.search", {
        "params": {
            "group": id
        },
        "onSuccess": function (data){
            for (var i = 0; i < data.length; i++)
                list += data[i].id + ",";
        }
    });

    /* If the list exists... */
    if( typeof list === "string" && list.length > 0 ) {
        list = list.slice(0,-1);

        /* Run an AJAX call to our PHP script which simply returns an integer
            value of the SUM of reward points earned by that list of students */

        UWA.Data.getJson(HighestClass.url + list, function(res){
            if (res === false || res === "") res = 0;

            /* Push this data into an array of objects alongside the class name */

            var obj = { "name": name, "points": res };
            HighestClass.array.push(obj);
        });
    }

    /* As this function is being called asynchronously multiple times we need to
        determine when the last call is run so that we can deal with our array
        of data. We do this thanks to the count/run_through variables in earlier
        function which will trigger end=true in this function */

    if( end === true )
        HighestClass.display();
}

HighestClass.display = function() {
    /* Once we've put our array of objects together, we need to sort it so that
        the class with the highest number of points are at array entry 0 */

    function compare(a,b) {
        if (a.points < b.points)
            return 1;
        if (a.points > b.points)
            return -1;

        return 0;
    }

    /* IF I PUT AN ALERT HERE, INTERNET EXPLORER WORKS, LOL? */

    HighestClass.array.sort(compare);

    /* We can then display the data of array entry 0 which should be our highest
        point-scoring class */

    $('#display').html( '<h1>' + HighestClass.array[0].name + '</h1><h3>' + HighestClass.array[0].points + '</h3>' );
}

/* equivalent of document ready */
widget.onLoad = function(){
    /* Choose the year group from the drop down box */
    $("select").change(function(){
        var val = $('select option:selected').val();

        $("#display").html('<h1><img width="60" height="60" src="http://logd.tw.rpi.edu/files/loading.gif" />Loading...</h1>');

        HighestClass.init(val);
    });
}

In essence the script does the following:

  1. Retrieve a list of students for each class group
  2. Run an AJAX call to our PHP script/MySQL database to return the SUM of points for those students
  3. Add the name and points info to an array of objects
  4. Sort the array of objects so that the highest point-scoring class is our first array entry
  5. Display the name of the class and their points from array entry 0

The problem is, the only way I can think about doing it (because of limitations of the APIs) is to run asynchronous API calls and chain AJAX calls off these. I then use a counting variable to determine when the last asynchronous call is made.

Now, importantly, this script works perfectly well in FireFox . However, in Internet Explorer - which is where I need it to work - the script displays our "loading" DIV/image and goes no further.

The strange thing is, if I put an alert in the code (where I've commented it in capital letters), Internet Explorer works correctly.

This must be an issue with synchronicity and timing but I have no experience or knowledge of it.

Can anyone suggest a solution? Hacky is fine, if necessary.

Cheers,

First thing is: /!\\ When use the callback pattern, your "flow" need to re-begin in the callback function.

I can see that you have problems with the Asynchronous and callback approach. When you $.getJSON but also every time you make a Frog.API call, example:

Frog.API.get("users.search", {
    "params": {
        "group": id
    },
    "onSuccess": function (data){
        for (var i = 0; i < data.length; i++)
            list += data[i].id + ",";
    }
});

Here you retrieve data, and put them in a list with the onSuccess callback function. My guess is that this call is also asynchronous. If this call takes too long:

 if( typeof list === "string" && list.length > 0 ) {

won't pass. So nothing will happening and your display will try to get values of an undefined object => error, the JavaScript stops, no update of your view. You need to getJSON after your list is retrieve, in the onSuccess callback. And this will help because you make the same mistake after:

In what follow you ask to have a display, but you absolutely don't know if your calls are finished. The fact it asked for the last call does not mean any of the calls are finished.

if( end === true )
    HighestClass.display();

So you just need to add that after:

HighestClass.array.push(obj);

which is in your $.getJSON call.

Ajax call are usually Asynchronous and your problem is that you try to update the display synchronously with the current flow, without waiting for your server to answer. /!\\ When use the callback pattern, your "flow" need to re-begin in the callback function. Using that, you will always be sure that the code you are running has all the data it needs to achieve it's duty.

PS: Here is all the code modified. I also modified your function init. You do not need to call your API again to redo the same thing. just loop twice on the data or put the only relevant data aside in an array then loop on it.

HighestClass = {};
HighestClass.array = [];
HighestClass.url = "http://our.url.local/frog/pointsByWeek.php?cmd=highestClass&students=";

HighestClass.init = function(groupPrefix) {

    /* Using the group prefix, i.e. "CLS 9", from the drop-down box,
        get a list of all of the classes in that year group */

    Frog.API.get('groups.getAll',{
        'onSuccess': function(data){
        var i = 0,
            l = 0,
            count = 0,
            group = [];
        /* First time round, count the number of groups that match this
            syntax because there are no parameters available to filter
            this API */
        for (i = 0, l = data.length; i < l; i++) {
            if (data[i].name.indexOf(groupPrefix) != -1)
                group.push(data[i]);
        }

        /* Now that these classes have been counted, run through the API
        call again to push each class ID through to another function */
        l = group.length;
        count = l - 1;
        for (i = 0; i < l; i++) {
            // i == count will be true when it is the last one
            HighestClass.getClassPoints( group[i].name, group[i].id, i == count);
        }
    });


}

HighestClass.getClassPoints = function(name, id, end) {

    /* Using the ID of the class group, create a comma-separated list
        of students for use in our MySQL query */
    Frog.API.get("users.search", {
        "params": {
            "group": id
        },
        "onSuccess": function (data){
            var list = '';
            // We have data and build our string
            for (var i = 0; i < data.length; i++)
                list += data[i].id + ",";

            /* If the list exists... */
            if( typeof list === "string" && list.length > 0 ) {
                list = list.slice(0,-1);

                /* Run an AJAX call to our PHP script which simply returns an integer
                    value of the SUM of reward points earned by that list of students */
                UWA.Data.getJson(HighestClass.url + list, function(res){
                    if (res === false || res === "") res = 0;

                    /* Push this data into an array of objects alongside the class name */

                    var obj = { "name": name, "points": res };
                    HighestClass.array.push(obj);


                    /* As this function is being called asynchronously multiple times we need to
                        determine when the last call is run so that we can deal with our array
                        of data. We do this thanks to the count/run_through variables in earlier
                        function which will trigger end=true in this function */

                    if( end === true )
                        HighestClass.display();
                });
            }
        }
    });



}

HighestClass.display = function() {
    /* Once we've put our array of objects together, we need to sort it so that
        the class with the highest number of points are at array entry 0 */

    function compare(a,b) {
        if (a.points < b.points)
            return 1;
        if (a.points > b.points)
            return -1;

        return 0;
    }

    /* IF I PUT AN ALERT HERE, INTERNET EXPLORER WORKS, LOL? */

    HighestClass.array.sort(compare);

    /* We can then display the data of array entry 0 which should be our highest
        point-scoring class */
    if (HighestClass.array.length > 0)
        $('#display').html( '<h1>' + HighestClass.array[0].name + '</h1><h3>' + HighestClass.array[0].points + '</h3>' );
    else
        $('#display').html( '<h1>No data available</h1>' );
}

/* equivalent of document ready */
widget.onLoad = function(){
    /* Choose the year group from the drop down box */
    $("select").change(function(){
        var val = $('select option:selected').val();

        $("#display").html('<h1><img width="60" height="60" src="http://logd.tw.rpi.edu/files/loading.gif" />Loading...</h1>');

        try {
            HighestClass.init(val);
        } catch (e) {
            $("#display").html('<h1>Sorry, an error occured while retrieving data</h1>');
        }
    });
}

The fact that an alert "fixes" the problem does indicate that it's something to do with a timing issue. It looks like one of your functions isn't returning in time and not populating the array variable correctly.

Try making the count and end variables global and seeing if that helps. I think it's something to do with scope.

It's most likely because your Ajax call is async here:

UWA.Data.getJson(HighestClass.url + list, function(res){
            if (res === false || res === "") res = 0;

            /* Push this data into an array of objects alongside the class name */

            var obj = { "name": name, "points": res };
            HighestClass.array.push(obj);
        });

and HighestClass.array is empty at when HighestClass.display(); is called unless you wait for your ajax call to complete. You can make your ajax call synchronous or put this HighestClass.display(); in the Ajax callback.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM