2

I am writing a javascript program with a very long-running foreach loop like the following:

for (property in object) {
    difficultTask();
}

I've seen that you can do some things with setTimeout which can allow you to periodically return control to the browser; however, I haven't seen any that are able to do this with a foreach loop, only a for loop with an index. Additionally, these tasks cannot be completed asynchronously since each iteration depends on a result from the previous iteration.

One solution I can think of is to split up my object into a lot of smaller objects and iterate through each of them, setting a timeout in between each one, but I'd like to see if this is possible without resorting to that.

Is there a way to do this without drastically changing how I have my objects?

quazzieclodo
  • 831
  • 4
  • 10
  • Is it he iteration that takes a while or the `difficultTask`? In other words, do you have an astronomical amount of properties, or are you running cycles on the functionality. – zamnuts Nov 19 '13 at 22:12
  • 1
    If the environment you run the code in supports `Object.keys`, you can use it convert the `for...in` loop to a `for` loop and apply the methods to it you have seen before. – Felix Kling Nov 19 '13 at 22:12
  • @zamnuts It's both, unfortunately. – quazzieclodo Nov 19 '13 at 22:14
  • 1
    You can put your code in a web worker. What exactly *is* "difficultTask" here? Anything compute bound for long enough to cause that browser warning on a modern computer is either some particle physics experiment or a broken/bad algorithm. – Pointy Nov 19 '13 at 22:15
  • @Pointy It's a large NLP task – quazzieclodo Nov 19 '13 at 22:18
  • @quazzieclodo ok that probably counts as particle physics :) – Pointy Nov 19 '13 at 22:19
  • @FelixKling Interesting, I'll have to check that out. – quazzieclodo Nov 19 '13 at 22:19
  • The web worker deal is that you put your code in the worker, and it's "firewalled" from the page environment. You send it a message, it gets to work concocting the perfect response tweet or whatever, and then it sends a message back to the page. – Pointy Nov 19 '13 at 22:21

3 Answers3

4

Get all the properties into an array and then you can cycle through the array with an index variable and a setTimeout():

var props = [];
for (property in object) {
    props.push(property);
}

var index = 0;
function nextChunk() {
    if (index < props.length) {
        difficultTask(object[props[index++]]));

        // schedule the next chunk of work after we let the browser process events
        setTimeout(nextChunk, 1);
    }

}
nextChunk();

You could also get all the props into an array with this is you don't need IE7 or IE8 compatibility:

var props = Object.keys(object);

Depending upon what the long running task does, web workers could also be an interesting option, but no support in IE until IE 10.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Ahhh, of course! I'll check out web workers too. This is mostly for an academic CS community, so I wouldn't imagine I'd need a lot of IE support... – quazzieclodo Nov 19 '13 at 22:23
  • @quazzieclodo - the limitation of web workers (beyond IE support) is that they can't access the DOM or any variables from your main JS thread so they can pretty much only do stand-alone computation. But, they work well if your needs fit into that limitation. – jfriend00 Nov 19 '13 at 22:25
  • hmmm... Not quite. Thanks! – quazzieclodo Nov 19 '13 at 22:27
1

I think that this snippet can achieve what you want:

function executeAndSleep(array,currentPos,step,sleepTime){
   for(var i = currentPos;i<currentPos+step&&i<array.length;i++){
      //execute your magic here
   }
   if(currentPos+step<= array.length){
       setTimeout(function(){
             executeAndSleep(array,currentPos+step,step,sleepTime);
        },sleepTime);
   }
}

you can see it working here http://jsfiddle.net/victorrseloy/3utwF/

1

This sounds like a useful point to use web workers. They allow concurrent execution of javascript code. You might want to read the introduction to webworkers. The article is quite old (might now be outdated in some points), but still good.

The browser support is now pretty good. If you need browser support for old IEs, you can use a polyfill, so you can start working with web workers right now.

Christoph
  • 50,121
  • 21
  • 99
  • 128
  • 1
    webworkers requires IE 10 which still makes it difficult to rely on for most sites that at least want to support IE 9. – jfriend00 Nov 19 '13 at 22:24
  • @jfriend00 there are enough polyfills available, so that's not really a problem... – Christoph Nov 19 '13 at 22:26
  • 1
    That polyfill is a polyfill in API only - it doesn't actually implement threading or chunking so thus it would not solve the OP's issue. I don't think it's possible to have a polyfill that implements threading. – jfriend00 Nov 19 '13 at 22:44
  • @jfriend00 of course it's not possible to implement threading this way, but that is not necessary, one could just implement the timeout slicing mechanism to prevent the UI freeze. I was just referring to the possibility to be able to write consistent code working in all browsers while still getting the benefit. – Christoph Nov 19 '13 at 23:39
  • But, if you have to break your work into chunks anyway with time slices, then a web workers polyfill isn't needed. I just didn't understand how a non-threading polyfill would help the OP in any way. That polyfill looks nice only for a script that would give an improved experience in a webworker, but does not need to be in a webworker. The OP's code needs to either be time sliced or in an actual webworker, it can't use a fake webworker polyfill. – jfriend00 Nov 19 '13 at 23:47
  • @jfriend00 It helps by being able to write browser independent code. The code will work in all browser - it's just a lot slower in the old IEs. In this "special" case where the computational part is so heavy to freeze the UI, you need aditional adaptions - but I'm speaking of the general case. Regardless of whether it's applicable here, I think webworkers are worth being mentioned. – Christoph Nov 19 '13 at 23:51
  • @jfriend00 well, that was not clear when I posted my answer. The OP did not state anything about the Task, so one can assume, webworkers might be a viable option. Of course, in the comments to your answer it then was stated, but that was some time after I posted my answer. Nonetheless, if you really think you need to be childish and downvote the answer, you're welcome. – Christoph Nov 19 '13 at 23:57
  • You don't seem to understand my point. Webworkers don't exist in IE before IE 11. So, if they don't change their code to chunk it, then your solution with even with polyfill will not solve their problem in any way because the browser will still become unresponse in IE before IE11 even with your polyfill. And, if they go to the bother of chunking their code, then webworkers are not needed at all. So, webworkers + polyfill just doesn't solve their problem (because it doesn't work in IE before IE11) regardless of whether their code is suited to a webworker. – jfriend00 Nov 20 '13 at 00:18
  • @jfriend00 You are right, except that web workers are already available for IE10. I understand your point very well, but I think it's not worth discussing any longer. Have a nice day. – Christoph Nov 20 '13 at 08:39