2

This is a follow-up question to: How to suspend JavaScript to allow render

I wrote a function that does a kind of suspended for-loop (to allow rendering in between):

function loop(count, callback){
    var counter = 0
    var iteration = function(){
        callback(counter)
        counter ++;
        if (counter < count){
            setTimeout(iteration, 0)
        }
    }
    iteration();
}

This works nicely, using e.g. loop(100, function(i){ console.log(i) } ). And allows the browser to render in between each iteration.

However, problems occur when executing stuff after the loop, since there is no guarantee that the loop has finished. Actually, I'm thinking there is a guarantee that it has not (since the main thread continues after setTimeout has been called the first time). This is exacerbated when nesting these kind of loops:

loop(10, function(i){
    loop(10, function(j){
        console.log(i + '-' + j)
    })
})
/// This definitely does not output the numbers in order...

My use case is that I am running a number of simulations, each consisting of a number of steps, and I want to render in between each. Am I going about this the wrong way?

4
  • You could take a look at this answer, which also discusses synchronizing asynchronous code. Commented May 15, 2013 at 10:11
  • If you want to execute something after loop, use something like function loop(count,callback,callback_finish){if(counter<count){}else{callback_finish()}}. Commented May 15, 2013 at 10:22
  • "and I want to render in between each" Why is that necessary with a timeout of 0? Commented May 15, 2013 at 13:54
  • Well, in my (limited) understanding, setting a timeout of 0 returns control to the browser thread, allowing it to do such stuff as redraw the DOM. Then, because the timeout is 0, as soon as possible, execution of the simulation will resume. Commented May 15, 2013 at 16:14

2 Answers 2

2

Here's how I would do it:

Edit: Updated answer after reading OP's comment below:

function loop(count, callback, done) {
    var counter = 0;
    var next = function () {
        setTimeout(iteration, 0);
    };
    var iteration = function () {
        if (counter < count) {
            callback(counter, next);
        } else {
            done && done();
        }
        counter++;
    }
    iteration();
}

loop(100, function (i, next) {
    console.log(i);
    next();
})


loop(10, function (i, nextI) {
    loop(10, function (j, nextJ) {
        console.log(i + '-' + j);
        nextJ();
    }, nextI);
});

Output:

1
1-1
2
1-2
3
1-3
4
1-4
5
1-5
6
1-6
7
1-7
8
1-8
9
1-9
10
11
2-1 
<< ... >>
97
98
99

http://jsfiddle.net/4Rw2H/1/

The loop function can have a third argument done which will be run after the loop is complete, if present. The callback will have a second argument next, which needs to be called when you want to run the next iteration.

This way, you can call an async function inside the loop, and still let it run in order.

Sign up to request clarification or add additional context in comments.

4 Comments

Looks good, though it is kind of hard to grasp. But if I copy this literally I'll lose the 'suspend' functionality inside loop, no? The goal of the loop function was to make a suspended-loop one-liner.
@Noio, I'm sorry, I don't understand what you mean by 'suspend' functionality? I think the functionality is probably identical to the code you posted in your question.
The purpose of my original loop function was to call each next iteration with setTimeout(0) to allow the browser to render the DOM. So your function is identical probably only if I call setTimeout(nextJ,0)?
You are increasing the counter before comparing it! Try loop(1, console.log) :-/
0

This seems to work properly:

JSFIDDLE

var log = function (i) { console.log(i) },
    loop = function (count, callback) {
        callback(count);
        count--;
        count > 0 && arguments.callee(count, callback);
    }

loop(10, log);
console.log('end');
loop(5, log);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.