开发者

JavaScript array management mystery

Brace yourself for a confusing explanation. I'm making a stupid little quiz site. Five question on the page. Answers stored in an xml file. Questions are displayed to players one at at time with an input for them to answer. When the player makes a guess for a particular answer, JQuery posts the guess to a php file that checks the answer against what is stored in the xml, and returns either 1 (correct) and or 0 (wrong). The post is triggered by the text input's keyup event.

I have a global array called remaining that stores the questions on the page that are still to be answered. This is populated on page load with the values [1, 2, 3, 4, 5]. As questions are answered correctly, the appropriate number is removed. So if the player answers question one, remaining will contain [2, 3, 4, 5].

var current;
var remaining;

$(document).ready(function() {

    // What question the player is on.
    current = 1; 

    // Questions unanswered.
    remaining = new Array(1, 2, 3, 4, 5);

    $('#in').keyup(function() {
        // Send answer to server to check if it's right.
        CheckGuess($(this).val());  
    });

});

function CheckGuess(guess) {

if (guess.length > 2 && guess.length < 100) 
{
    $.post( 
        "class/check.php",
        "current=" + current + "&answer=" + guess,
        function(check) {

             if (check == 1) {
                    AnswerCorrect();
             }

        },
开发者_如何学运维        "json"
    );
}   
}

function AnswerCorrect() {

// User guessed correctly.

if (remaining.length != 1) {

    var next;

    // Remove complete question from array of remaining values.
    for (var i = 0; i < remaining.length; i++ ) {
        if (remaining[i] == current) {          

            // Set the next question. It will be the next one in the array 
            // or the previous if the current question is the last in the array.
            if (i != (remaining.length - 1)) {
                next = remaining[i + 1];
            } else {
                next = remaining[i - 1];
            }

            debugger;

            // Remove current question.
            remaining.splice(i, 1);

            // Get out of the for loop.
            break;

        }           
    }   

    // Set current as next.
    current = next;

    // Set the href for the next question.
    var destination = "#m" + next;

    // Scroll to next question.
    $.scrollTo($(destination), { duration: 1000});

    // Clear input box.
    $("#in").val("");

}

else {
    // Quiz complete.
}

}

It all works to some extent. But I'm having horrible trouble with some mysterious issue. I've stepped through it with FireBug and what seems to happen is:

  • (a). Page loads. I can see remaining has the values [1,2,3,4,5] as I want.
  • (b). Player enters correct answer for question one. "1" is then removed from remaining so it has values [2,3,4,5] as expected.
  • (c). Player enters correct answer for question two, but now as soon as FireBug hits any breakpoint I have set I can see that remaining has the values [3,4,5]. So before the function AnswerCorrect() is called 2 is gone. Where did 2 go?! Then when AnswerCorrect() does actually run it thinks the player is on question 3 (because remaining contains [3,4,5]). The overall result is that when the player answers question 2, both 2 and 3 are marked as correct.

I hope my explanation was somewhat clear. I've never understood anything less in my life. I don't understand what happens between point (b) and (c) above. I'm stepping through the code and I can't find where remaining drops "2". FireBug is letting me down. It doesn't seem to be breaking somewhere when it should. If I have a breakpoint on the split() I don't see the removal of 2 could be going on without me seeing it. Anyone have any clue? I'd really appreciate any help before I go mad.

EDIT - More Info

(Sorry for the slow reply, I was at work all day).

The real issue is that I can't see where the 2 is being dropped. One thing I was thinking that may be the problem (but I don't know enough about Javascript to know if this is possible):

The jQuery post fires frequently; every time the player enters a letter. Is it possible that AnswerCorrect() could be called multiple times simultaneously? So that two or more "instances" of AnswerCorrect() are running concurrently?

EDIT 2

I decided to give up on this method. I can't figure out the problem. I rewrote it so that now the post occurs on document load and the answers are stored server-side in an array. That's probably a better way of doing it anyway, since I only have one post to the server instead of many. And it all works fine now. Consider this thread solved.


I think you're heavily overcomplicating things. There is no need for a loop or a splice, and in your looping you're mixing array index with array value in attempting to figure out where you are. It is very very confusing--even if you had it figured out, the next guy to come along and maintain it would be lost (even if that's you 6 months from now).

You want the first item off the array, and you want the array length modified accordingly. For this you should use Array.prototype.shift(). I've rearranged some of your code for better practices, but tried to leave some of it in the same layout so as to avoid making it unrecognizable to you:

var remaining = [ 1, 2, 3, 4, 5 ];

$( function()
{
    $( '#in' ).keyup( function()
    {
        // Send answer to server to check if it's right.
        CheckGuess( $( this ).val() );  
    } );

    Next();
} );

function CheckGuess( guess )
{
    if( guess.length > 2 && guess.length < 100 )
    {
        $.post( 
            'class/check.php',
            {
                current: + current,
                answer + guess
            },
            function( check )
            {
                if( check == 1 )
                {
                    Next();
                }
            },
            'json'
        );
    }   
}

function Next()
{
    // User guessed correctly.
    if( remaining.length )
    {
        current = remaining.shift();

        // Scroll to next question.
        $.scrollTo( $( '#m' + current ), { duration: 1000 } );

        // Clear input box.
        $( '#in' ).val( '' );
    }
    else
    {
        // Quiz complete.
    }
}


I don't honestly see any logic errors in the code you have posted. I put it in a jsFiddle here with a simulated async check for correct and a view of the remaining answers every time. It seems to work for me. So, if your real code is having this issue you're talking about, then there must be something else to the code that you haven't shown us here.

One typical place that things can go wrong in an app with an asynchronous ajax call like this and some global state is that the async part might not be done right. It has to wait for the success handler of the server call before changing any state. If state is changed before that processes, then it may mess things up when it finishes.

What the other two answers have suggested are simpler ways to write the code if you can assume that the first question in the array is always the one being worked on. Your AnswerCorrect() code does not assume that the only answer being worked on is the first on in the array and is thus more general so if that is the case, then I don't think you can use the code from those other answers.

So, I think the answer to your question is that there must be more to what is causing the error than you have disclosed in your question because I don't see any serious logic error with what you have posted and the "simulated" jsFiddle that uses your code seems to work. So, what else is there in your app that you haven't shown us here?

EDIT:

The most common mistake when first using ajax calls is related to the asynchronous nature of the ajax call. When you make the call, it STARTS the ajax call and it happens in the background while the rest of your code keeps running. It does not finish until sometime later when the completion/success function is invoked. If you forget that fact and you do other things after you STARTED the ajax call BEFORE it has completed, then your logic will go awry. The sample you've posted here doesn't have that problem, but it would be my guess that there is more to your real code after the POST to the server and perhaps that is the source of the error/confusion. Since you are using some global variables to keep track of state, if you touch any of those global variables after the POST call and before the completion function is called (like to move to the next question), it will mess up your logic. The success function will come through and your global state will have been incorrectly altered. That's just a guess because all I have to go by is the code you've posted which doesn't have the problem you are experiencing.

If you want to post the real code or give us a link to the real page, we can take a look there.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜