简体   繁体   中英

Sequentially running multiple setIntervals

I'm trying to run multiple setInterval() 's but I'm having trouble with the asynchronous aspect of doing this.

This example is almost identical to what I'm trying to achieve:

var labels = ["Bacon", "Beer", "Steak"];
var second, timer;
labels.forEach(function(label){
    second = 0;
    timer = setInterval(function(){
        console.log(label + ' is awesome ' + second);
        if(second === 10) {
            clearInterval(timer);
        }
        second++;
    }, 1000);
});

Because of setInterval being asynchronous the three intervals are running in parallel and are messing up the interval:

Bacon is awesome 0

Beer is awesome 1

Steak is awesome 2

Beer is awesome 3

Bacon is awesome 4

Steak is awesome 5

Bacon is awesome 6

Beer is awesome 7

Steak is awesome 8

Bacon is awesome 9

Beer is awesome 10

Beer is awesome 11

Bacon is awesome 12

Beer is awesome 13

Bacon is awesome 14

Bacon is awesome 15

Beer is awesome 16

Beer is awesome 17

Bacon is awesome 18

Bacon is awesome 19

Beer is awesome 20

Beer is awesome 21

Bacon is awesome 22

Bacon is awesome 23

...

I'm wondering how to enforce some sort of queue so that the first interval runs it's course then when 10 seconds are up the next setInterval is executed?

I've tried quickly with the async.js library but I had trouble finding a suitable control flow.

This was good for me to practice some OO style Javascript :)

    function Timer(name) {
    var self = this;
    this.name = name;
    this.count = 0;
    this.x = setInterval(function () {
        if (self.count++ == 9) {
            clearInterval(self.x);
        }
        console.log(self.name + ' ' + self.count + ' is awesome');
    }, 1000);
}



var labels = ["Bacon", "Beer", "Steak"];
labels.forEach(function (label) {
    new Timer(label);
});

I made a little function called `callQueue' that calls an Array filled with functions in order a certain number of times with a certain interval between them

Usage

The first argument is the array of functions, the second the number of times they should be called, the third is the time between the calls.

callQueue([

function () {
    isAwesome('bacon');
},

function () {
    isAwesome('beer');
},

function () {
    isAwesome('steak');
}], 10, 1000);

The Function

function callQueue(queue, times, timeBetween) {
    var i = 0;
    k = 0;
    queue.forEach(function (func) {
        for (k = 0; k < times; k += 1) {
            alert(i * timeBetween);
            i += 1;
            setTimeout(func, (i * timeBetween));
        }
    });
};

Demo: http://jsfiddle.net/howderek/L5nhU/

You could eg use setTimeout without a loop at all, and pick the right label from within the function.

var labels   = ["Bacon", "Beer", "Steak"],
    steps    = 10,
    total    =  0,
    step     =  0,
    sequence =  0,
    label    = labels [0];

(function fn (){
    if (step === steps && sequence === labels.length - 1) {
        return;
    } else if(step === steps) {
        label = labels [++sequence];
        step  = 0;
    }  

    console.log(label + ' is awesome ' + total);

    step++;
    total++;
    setTimeout (fn, 1000)
})();

Fiddle

Or eg keep the loop and use the index and your number of steps as multiplier as timeout, to delay the other timeouts accordingly.

var labels = ["Bacon", "Beer", "Steak"],
    steps = 10,
    total = 0;
labels.forEach(function(label, index){
    var step = 0;
        setTimeout (function fn (){
            if(step === steps) {
                return;
            }

            console.log(label + ' is awesome ' + total);

            step++;
            total++;
            setTimeout (fn, 1000)
        },( (steps + 1)* index ) * 1000);
});

Fiddle

Or wrap your setInterval in a setTimeout .

var labels = ["Bacon", "Beer", "Steak"];
labels.forEach(function(label, index){
    setTimeout (function () {
           var second = 0;
           var timer = setInterval(function(){
                console.log(label + ' is awesome ' + second);
                if(second === 10) {
                    clearInterval(timer);
                }
                second++;
            }, 1000);
    },index * 11 * 1000);
});

Fiddle

All produce

Bacon is awesome 0
Bacon is awesome 1
Bacon is awesome 2
Bacon is awesome 3
Bacon is awesome 4
Bacon is awesome 5
Bacon is awesome 6
Bacon is awesome 7
Bacon is awesome 8
Bacon is awesome 9
Beer is awesome 0
Beer is awesome 1
Beer is awesome 2
Beer is awesome 3 
... 

Once you've called setInterval and setTimeout you're in the hands of the browser; the time that passes isn't guaranteed to be exact and the order that multiple callbacks are executed in aren't guaranteed to be exact - and usually won't be.

If you want to enforce order of execution, using setTimeout instead of setInterval will give you the opportunity to decide what happens when each callback completes.

Here's a fiddle that iterates over the Bacons first, then Beers, then Steaks: http://jsfiddle.net/q96Vc/

And here's one that displays each label one after the other until 30 seconds is up: http://jsfiddle.net/DtbZ6/1/

You could even expand on either of these examples to use three separate calls to setTimeout that won't get repeated until the three previous calls have all been executed, which would mean the order of the three would vary but they would never overlap.

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