soundswaste
soundswaste

Reputation: 3074

Javascript : Do Action after elapsed time

I have a page where I fire an ajax request AND show a loading sign for the first 6 seconds after the user presses a button :

<button onclick="runLoader();"></button>

<script>
    var startTime=(new Date).getTime();
    function runLoader(){
        runAjax();
        var i=0;
        var timer = setInterval(function(e){
            i++;
            //code for loading sign
            if(i==3)
                clearInterval(timer);
        } ,2000);
    }

    function runAjax(){
        $.ajax({
            //
        }).done(function(){
            var timer2 = setInterval(function(){
                var d = new Date();
                var t = d.getTime();
                if(t-startTime>=6000){
                    clearInterval(timer2);
                    // do X
                }
            },500);
        }
    }
</script>

I want to do action X only after both runLoader() has run for 6 seconds and runAjax() has resulted in a response, and no sooner.

Like, if runAjax() responds in 2 seconds, I still want to continue showing loading sign for 6 seconds and then perform X. And if the loading sign has shown for 6 seconds, I want to wait for runAjax() to return for as long as it takes.

But using the Date() method is giving inaccurate results. For eg : It shows 7.765 s elapsed even when only 2 s have passed. I read somewhere I should use console.log(time) for better accuracy, but it doesnt work in <=IE9.

Is there a better way to approach this problem ?

Note: I am using setInterval() instead of setTimeout() because the loading involves cycling through an array of 3 elements, "Fetching", "Processing" and "Loading" each shown for 2 seconds :)

Upvotes: 1

Views: 1147

Answers (3)

Jason P
Jason P

Reputation: 27012

I would use deferreds and $.when:

function start(){
    $.when(runLoader(), runAjax()).done(function() {
        //both are now finished
    });
}

function runLoader() {
    //show loader here

    var def = $.Deferred();

    setTimeout(function() {
        //hide loader here
        def.resolve(true);
    }, 6000);

    return def.promise();
}

function runAjax() {
    var def = $.Deferred();

    $.ajax({...}).done(function(result) {
        //handle response here
        def.resolve(true);
    });

    return def.promise();
}

Upvotes: 8

Kroltan
Kroltan

Reputation: 5156

You can create a pre-caller function that is run on runLoader() and as callback of runAjax(), that will verify if the other action is complete, and then do action X. Example:

var ajaxDone = false;
var loaderDone = false;
function doActionX() {
    //your action happens here
}
function tryToDoX() {
    if (ajaxDone && loaderDone) {
        doActionX();
    }
}
function runLoader(){
    loaderDone = false;
    runAjax();
    //show loading sign
    setInterval(function(e){
        //hide loading sign
        clearInterval(timer);
        loaderDone = true;
        tryToDoX();
    }, 6000);
}

function runAjax(){
    ajaxDone = false;
    $.ajax({
        //whatever
    }).done(function(){
        ajaxDone = true;
        tryToDoX();
    }
}

It isn't necessary to make a recurring timeout and poll both statuses, because they only get completed once (in every run, i.e. booleans aren't set to false and true while waiting).

EDIT: This approach can be used to any asynchronous code that doesn't change status intermitently, even without jQuery.

Upvotes: 0

Smeegs
Smeegs

Reputation: 9224

I would set a flag to mark it as "ready". There may be better ways to handle this, but this is just off the top of my head.

<script>
    function runLoader(){
        runAjax();
        var i=0;
        var timer = setInterval(function(e){
            i++;
            //code for loading sign
            if(i==3)
                clearInterval(timer);
        } ,2000);
    }

    function runAjax(){
        var timeElapsed = false;
        setTimeout(function(){timeElapsed = true}, 6000);
        $.ajax({
            //
        }).done(function(){
            var timer2 = setInterval(function(){
                if(timeElapsed){
                    clearInterval(timer2);
                    // do X
                }
            },500);
        }
    }
</script>

Upvotes: 0

Related Questions