Breaking a loop from outside - javascript

I have a data set, over which I iterate and run a fairly heavy operation on each element. To prevent the browser from freezing, I've set a timeout of 0ms. While the browser doesn't freeze, the calculation can take a good few seconds depending on user input. Here's the code:
function calculation(){
$.each(data,function(key,value){
setTimeout(function(){
doTheHeavyLifting(value);
},0);
});
}
Now, the problem is, that I want to stop the previous calculation if the user requests to run the calculation again with new values.
I tried defining a continueCalculating boolean outside the function, and setting that to false in runCalculation() before calling calculation(), and inside calculation() resetting it back to true. Here's the modified code:
var continueCalculating=false;
function runCalculator(){
continueCalculating=false;
calculation();
}
function calculation(){
continueCalculating=true;
$.each(data,function(key,value){
setTimeout(function(){
if(continueCalculating){
doTheHeavyLifting(value);
}else{
console.log("Abort the mission!");
return false;
}
},0);
});
}
Well, that didn't do the trick, and in retrospect was a pretty silly idea anyway. So, next I tried also clearing all the timeouts. Here's the modified code:
var continueCalculating=false;
var operationQueue=[];
function runCalculator(){
continueCalculating=false;
$.each(operationQueue, function(k,v){
clearTimeout(v);
});
calculation();
}
function calculation(){
continueCalculating=true;
$.each(data,function(key,value){
operationQueue.push(setTimeout(function(){
if(continueCalculating){
doTheHeavyLifting(value);
}else{
console.log("Abort the mission!");
return false;
}
},0));
});
}
Well, that didn't yield any results either.
I know $.each is slower than for, but there are only about a couple dozen items to iterate over, so it certainly isn't the bottle neck here. I'm not looking for ways to optimize this code, I just need to stop an on-going calculation. Is this possible?

You should iterate asynchronously:
var data = [0,1,2,3,4,5,6,7,8,9,"end"];
function doTheHeavyLifting(value) {
console.log(value);
}
function calculation(data) {
var key = 0, timer;
(function loop() {
if(key < data.length) {
doTheHeavyLifting(data[key]);
key++;
timer = setTimeout(loop, 1e3);
}
})();
return {stop: function() { clearTimeout(timer) } };
}
var calc = calculation(data);
document.querySelector('#stop').onclick = calc.stop;
<button id="stop">Stop</button>

You can use $.queue(), $.Deferred(), .promise()
// check at `if` condition within deferred
var stop = false;
$("button").on("click", function() {
// clear `"calculation"` queue at `click` event at `button` element
$.queue(data, "calculation", []);
// set `stop` to `true` at `click` event at `button`
stop = true;
});
// `data`
var data = Array(100).fill(0).map((v, k) => k);
function doTheHeavyLifting(val) {
console.log(val);
// return `.promise()` when process within `doTheHeavyLifting` completes
return $("body").append("\n" + val).promise()
}
function calculation() {
$.queue(data, "calculation", $.map(data, function(value, index) {
return function(next) {
return new $.Deferred(function(dfd) {
setTimeout(function() {
if (!stop) {
dfd.resolve(doTheHeavyLifting(value))
} else {
dfd.rejectWith(
$.queue(data, "calculation")
, ["queue stopped at " + index]
);
}
},0)
})
// iterate next item in `data`
.then(next)
// do stuff if `"calculation"` queue is empty array
.fail(function(message) {
console.log(message
, "calculation queue:", this);
alert(message);
})
}
}));
// start queue
$.dequeue(data, "calculation");
}
calculation();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<button>stop calculation</button>

Related

Complete all functions inside a FOR LOOP before iterating to next iteration of the loop

Say i have a function like :
var bigArray = [1,2,3,4,5.........n];
for(var i=0; i<bigArray.length; i++){
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
.
.
.
.
.
.
.
.
if(someCondition){
setTimeout(function(){
..do_stuff..
callSomeFunction();
}, someDelayTime);
}
}
Now what i want from this function is that when all of the conditions have performed their intended task, only then it should move to the next iteration of FOR loop. In other words, i=0 in FOR LOOP should change to i=1 and so-on iff all of the conditions inside the FOR loop have completed their job in current iteration.
Currently behaviour of this code is very random (because of setTimeout i believe). How can i make this work as per my expectations?
I recently read about promises (don't know much about them) but i'm not sure how to implement them in this code or if they will work in this case...
The original idea came from the answer of Nina Scholz on some similar question.
this answer is good if you don't like promises and deferred object. Othewise Kris Kowal's Q library would be a better choice.
function Iterator() {
var iterator = this;
this.queue = [];
this.add = function(callback, wait) {
iterator.queue.push(iterator.wait(wait));
iterator.queue.push(function() {
callback();
iterator.next();
});
};
this.wait = function(wait) {
return function() {
setTimeout(iterator.next, wait);
};
};
this.next = function() {
iterator.queue.length && iterator.queue.shift()();
};
}
var arr = [1, 2, 3, 4, 5],
counter = -1;
var iterator = new Iterator();
(function fillNextIteration() {
if (counter >= arr.length)
return;
counter++;
iterator.add(function() {
console.log('1 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('2 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('3 - counter value is ' + counter);
}, 100);
iterator.add(function() {
console.log('4 - counter value is ' + counter);
}, 100);
iterator.add(fillNextIteration, 100);
iterator.next();
})();
Code Explanation
Iterator class has one queue which is and array and some methods:
next
when you call next() if there is any callback in queue it will fetch first and execute that. when you call Array#shift it removes and returns first item from array. When this item is a function then you can invoke it by adding parentheses in front of it. here is the expanded version:
if (iterator.queue.length > 0) {
var callback = iterator.queue.shift();
callback();
}
wait
This method will add an extra callback to queue which after a timeout calls the next method. this one is for creating the expected delay.
add
This method calls the wait with desired delay then attaches another function to queue which will call the callback and then calls next to make callback chain running.
fillNextIteration
after explaining the iterator there is another loop here on function fillNextIteration this starts with the conditions for the first iteration for example:
if (someConditionIsTrue) {
iterator.add(function() {
doWhatShallBeDone;
}, delay);
}
you can check all conditions and then if required add the callback as shown.
after all condition finished add fillNextIteration as last action to continue the loop.
self invoking function
As you can see fillNextIteration is self invoked.
This helps to reduce one invoke and works like this:
function f(){};
f();
Last thing to mention
you can create a loop by calling a function inside itself. if there is no delay or stop for revoke then it would be a deadlock. So this is another loop as well:
(function loop() { setTimeout(loop, delay); })();
Try this please :
bigArray = [1, 2, 3, 4, 5.........n];
neededStuff = 10; // Number of finished works you need below
stuffOK = neededStuff;
function mainFunction(i) {
function loopFunction(i) {
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
if (someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
/*
...
*/
if(someCondition) {
setTimeout(function () {
// .. do_stuff..
stuffOK++;
callSomeFunction();
}, someDelayTime);
}
}
if (stuffOK == neededStuff) {
i++;
stuffOK = 0; // reinit counter
loopFunction(i);
}
setTimeout('mainFunction(' + i + ');', 100);
}
mainFunction(-1);
Hope this will work from promise chaning
callSomeFunction1().then(function() {
callSomeFunction2();
}).then(function() {
callSomeFunction3();
})

Calling a function recursively with setTimeout

I want call few function one after another recursively with setTimeout.
var flag = 0 ;
function slave1(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
//doing similar task
function slave2(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT2");
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave2,2000);
}
}
}
function master() {
slave1();
console.log("Without completing slave1 function.");
slave2();
}
Through master() function I want to call multiple functions one after another, however in current situation its calling slave2() without completing slave1(). How can I make sure that slave1() has executed completed. If DOM element is not loaded than it should execute 60 times after every 2 seconds and than it should come out from slave1() and go to next one.
I want to execute same function for 60 times if dom element is not loaded without returning the control to next function.
You need to adjust slave1 to run a callback when it is finished which will be slave2.
function slave1(callback){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
callback();
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
function slave2(){...}
function master() {
slave1(slave2);
console.log("Without completing slave1 function.");
}
This is your basic javascript chaining. If you have more slaves you might want to look into async.series otherwise you go into callback hell as Gabs00 has put it nicely:
slave1(function(){
slave2(function(){
slave3(function(){
slave4(slave5);
});
});
});
If you need to pass values to callbacks then you need to use an intermediate anonymous function which in turn calls the intended callback with the arguments in question. To do that, you need define your functions so that they use the arguments:
function slave1(str, callback){...}
function slave3(i, callback){...}
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using promises for things like that. Here an implementation on top of jQuery, other promise libraries work similarly.
function waitForElement(elementId, maxTries, checkInterval) {
var d = $.Deferred(), intvalID, checkFunc;
// set up default values
maxTries = maxTries || 60;
checkInterval = checkInterval || 2000;
checkFunc = function () {
var elem = document.getElementById(elementId);
if (maxTries-- > 0 && elem) {
clearInterval(intvalID);
d.resolve(elem);
}
if (maxTries <= 0) {
clearInterval(intvalID);
d.reject(elementId);
}
};
// set up periodic check & do first check right-away
intvalID = setInterval(checkFunc, checkInterval);
checkFunc();
return d.promise();
}
Now, if you want to test for elements one after another, you can cascade the calls like this:
function master() {
waitForElement("DOM_ELEMENT1").done(function (elem1) {
waitForElement("DOM_ELEMENT2").done(function (elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
}).fail(function () {
alert("elem1 exists, but elem2 was not found.");
});
}).fail(function () {
alert("elem1 not found.");
});
}
or you can do it in parallel and have a callback called when all of the elements exist:
function master() {
$.when(
waitForElement("DOM_ELEMENT1"),
waitForElement("DOM_ELEMENT2")
)
.done(function (elem1, elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
})
.fail(function () {
alert("not all elements were found before the timeout");
});
}
Your slave2 function should be passed to slave1 function as a callback and should be called in slave1 after it finishes (if ever?). Your current situation is quite common, since setTimeout() function is asynchronous, thus JS interpreter doesn't wait till the function is completed, but sets the setTimeout() result at the end of the Evet Loop and continues processing the master() method.
In order to pass arguments to functions, creating anonymous functions turns out to be an overkill. Consider using "bind" instead. So, if you've got
function slave1(str, callback){...}
function slave2(str, callback){...}
function slave3(i, callback){...}
function slave4(i, callback){...}
function slave5()
Instead of using
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using
slave1("some argument",
slave2.bind(null, "another argument",
slave3.bind(null, 1,
slave4.bind(null, 2, slave5)
)
)
);
Much easier, more efficient in terms of memory and CPU utilization.
Now, how to do this with setTimeout:
slave1("some argument",
setTimeout.bind(null, slave2.bind(null, "another argument",
setTimeout.bind(null, slave3.bind(null, 1,
setTimeout.bind(null, slave4.bind(null, 2,
setTimeout.bind(null, slave5, 0)
),0)
),0)
),0)
);
I explained the problem in more detail at
http://morethanslightly.com/index.php/2014/09/executables-the-standard-solution-aka-mind-the-bind/

Javascript: Force new loop iteration in setInterval

I have a setInterval loop. It's set to 3500 milliseconds, like so:-
var loop = setInterval(function() { /*stuff*/ }, 3500);
At one point in 'stuff' if a certain situation occurs, I want to force a new iteration of the loop and NOT WAIT for the 3500 milliseconds. How is that possible? Is it continue or do I just need to frame the process differently?
You could try writing an anonymous self-calling function using setTimeout instead of setInterval:
var i = 0;
(function() {
// stuff
i++;
if (i % 2 == 0) {
// If some condition occurs inside the function, then call itself once again
// immediately
arguments.callee();
} else {
// otherwise call itself in 3 and a half seconds
window.setTimeout(arguments.callee, 3500);
}
})();​ // <-- call-itself immediately to start the iteration
UPDATE:
Due to a disagreement expressed in the comments section against the usage of arguments.callee, here's how the same could be achieved using a named function:
var i = 0;
var doStuff = function() {
// stuff
i++;
if (i % 2 == 0) {
// If some condition occurs inside the function, then call itself once again
// immediately
doStuff();
} else {
// otherwise call itself in 3 and a half seconds
window.setTimeout(doStuff, 3500);
}
};
doStuff();
You can use something like this... using setTimeout instead of setInterval...
<script type="text/javascript">
var show;
var done = false;
show = setTimeout(showHideForm, 3500);
function showHideForm() {
// Do something
if(done) {
clearTimeout(show);
show = setTimeout(showHideForm, 2000);
}
}
</script>
clearTimeout takes as argument the handle which is returned by setTimeout.
Use a named function and call it when you want.
var loop = setInterval(loopFunc, 3500);
function loopFunc(){
//do something
}
function anticipate(){
clearInterval(loop); //Stop interval
loopFunc(); //Call your function
loop = setInterval(loopFunc, 3500); //Reset the interval if you want
}
My contrived example:
var time = 3500,
loops = 0,
loop;
(function run(){
var wait = time,
dontwait = false;
if (loops++ == 5) {
loops = 0;
dontwait = 1000;
}
console.log('Interval: ', dontwait || wait);
return loop = setTimeout(run, dontwait || wait);
})();​
http://jsfiddle.net/NX43d/1/
Basically, a self-invoking function looping back on a self-calling function, with (!) shorthand variable switching. Nifty.
function looper(t) {
var loop = setInterval(function() {
document.write(s++);
if (mycondition) { // here is your condition
loopagain(200); // specify the time which new loop will do
loop = window.clearInterval(loop); // clear the first interval
return; // exit from this function!
}
}, t);
}
window.onload = looper(1000); // this will have default setInterval function time ans will start at window load!
function loopagain(t) {
looper(t);
}​
http://jsfiddle.net/tFCZP/

Blocking "wait" function in javascript?

As part of a Javascript project I'm working on, there are some synchronous ajax calls (I guess that makes it "sjax", but I digress). I'm now writing a debugging panel which would allow me to test out the site with some artificially simulated network conditions by wrapping $.ajax. Simple things: faking a 500 response etc, and making the ajax calls take much longer.
For the asynchronous calls, it's simple. When the real response comes back, add a setTimeout to make it wait for the artificial response time before triggering the callback. However, this doesn't work with the synchronous calls obviously, since setTimeout isn't synchronous.
So, is there a way to make a Javascript program perform a blocking wait for a set amount of time?
The only thing I could think of would be something like this:
function wait(ms) {
var start = +(new Date());
while (new Date() - start < ms);
}
Is there a better solution?
(Also, please assume there's a good reason for the blocking ajax calls... :-\)
Do not do it on the JavaScript level. Get a proxy such as Fiddler and set up an AutoResponder to delay the call by a time period.
If it's just for debugging purposes to have an artificial delay:
alert('block me one more time');
There is no reasonable other approach to have a blocking code in ECMAscript. Since Javascript is executed in the same thread ("UI thread") which browsers use to render the DOM and to certain other things, the whole show was designed not to block anything.
Of course you can fake it by using a loop, but its a perversion of the show.
I figured this code might help
// execute code consecutively with delays (blocking/non-blocking internally)
function timed_functions()
{
this.myfuncs = [];
this.myfuncs_delays = []; // mirrors keys of myfuncs -- values stored are custom delays, or -1 for use default
this.myfuncs_count = 0; // increment by 1 whenever we add a function
this.myfuncs_prev = -1; // previous index in array
this.myfuncs_cur = 0; // current index in array
this.myfuncs_next = 0; // next index in array
this.delay_cur = 0; // current delay in ms
this.delay_default = 0; // default delay in ms
this.loop = false; // will this object continue to execute when at end of myfuncs array?
this.finished = false; // are we there yet?
this.blocking = true; // wait till code completes before firing timer?
this.destroy = false; // <advanced> destroy self when finished
// handle next cycle execution
this.next_cycle = function() {
var that = this;
var mytimer = this.delay_default;
if(this.myfuncs_cur > -1)
if(this.myfuncs_delays[this.myfuncs_cur] > -1)
mytimer = this.myfuncs_delays[this.myfuncs_cur];
console.log("fnc:" + this.myfuncs_cur);
console.log("timer:" + mytimer);
console.log("custom delay:" + this.myfuncs_delays[this.myfuncs_cur]);
setTimeout(function() {
// times up! next cycle...
that.cycle();
}, mytimer);
}
this.cycle = function() {
// now check how far we are along our queue.. is this the last function?
if(this.myfuncs_next + 1 > this.myfuncs_count)
{
if(this.loop)
{
console.log('looping..');
this.myfuncs_next = 0;
}
else
this.finished = true;
}
// first check if object isn't finished
if(this.finished)
return false;
// HANDLE NON BLOCKING //
if(this.blocking != true) // blocking disabled
{
console.log("NOT BLOCKING");
this.next_cycle();
}
// set prev = current, and current to next, and next to new next
this.myfuncs_prev = this.myfuncs_cur;
this.myfuncs_cur = this.myfuncs_next;
this.myfuncs_next++;
// execute current slot
this.myfuncs[this.myfuncs_cur]();
// HANDLE BLOCKING
if(this.blocking == true) // blocking enabled
{
console.log("BLOCKING");
this.next_cycle();
}
return true;
}; // END :: this.cycle
// adders
this.add = {
that:this,
fnc: function(aFunction) {
// add to the function array
var cur_key = this.that.myfuncs_count++;
this.that.myfuncs[cur_key] = aFunction;
// add to the delay reference array
this.that.myfuncs_delays[cur_key] = -1;
}
}; // end::this.add
// setters
this.set = {
that:this,
delay: function(ms) {
var cur_key = this.that.myfuncs_count - 1;
// this will handle the custom delay array this.that.myfunc_delays
// add a custom delay to your function container
console.log("setting custom delay. key: "+ cur_key + " msecs: " + ms);
if(cur_key > -1)
{
this.that.myfuncs_delays[cur_key] = ms;
}
// so now we create an entry on the delay variable
}, // end :: this.set.delay(ms)
delay_cur: function(ms) { this.that.delay_cur = ms; },
delay_default: function(ms) { this.that.delay_default = ms; },
loop_on: function() { this.that.loop = true; },
loop_off: function() { this.that.loop = false; },
blocking_on: function() { this.that.blocking = true; },
blocking_off: function() { this.that.blocking = false; },
finished: function(aBool) { this.that.finished = true; }
}; // end::this.set
// getters
this.get = {
that:this,
delay_default: function() { return this.that.delay_default; },
delay_cur: function() { return this.that.delay_cur; }
}; // end::this.get
} // end ::: timed_functions()
And Test...
// // // BEGIN :: TEST // // //
// initialize
var fncTimer = new timed_functions;
// set some defaults
fncTimer.set.delay_default(1000); // set a default delay between function blocks
fncTimer.set.blocking_on(); // next timer begins count before code is executed
fncTimer.set.blocking_off(); // next timer begins count after code is executed
// fncTimer.set.loop_on(); // when finished start over
// fncTimer.set.loop_off();
// BEGIN :: ADD FUNCTIONS (they will fire off in order)
fncTimer.add.fnc(function() {
console.log('plan a (2 secs)');
});
fncTimer.set.delay(2000); // set custom delay for previously added function
fncTimer.add.fnc(function() {
console.log('hello world (delay 3 seconds)');
});
fncTimer.set.delay(3000);
fncTimer.add.fnc(function() {
console.log('wait 4 seconds...');
});
fncTimer.set.delay(4000);
// END :: ADD FUNCTIONS
// NOW RUN
fncTimer.cycle(); // begin execution
// // // END :: TEST // // //

Coordinating Asynchronous Requests in Javascript

I want to get two resources using two asynch calls. I want to proceed only when both resources have been retrieved.
How can I do this elegantly in JS?
This would work:
getStuff1(function (result1) {
getStuff2 (function (result2) {
// do stuff with result1 and result2
....
}
}
but stuff2 only starts after stuff1 completes. I'd prefer to start stuff2 while waiting on stuff1.
If you know that functions are in fact first-class objects in Javascript, you can come up with a fairly elegant solution.
Without any extra objects, or global variables.
function callback1() {
callback1.done = true;
commonCallback();
}
function callback2() {
callback2.done = true;
commonCallback();
}
function commonCallback() {
if (callback1.done && callback2.done) {
// do stuff, since you know both calls have come back.
}
}
Why is this so elegant? Because you've encapsulated the data, your scope is free from useless variables and the code is more readable than ever. How cool is that? :)
UPDATE
And if you want a bit more general solution you may try the following:
function callback() {
callback.number -= 1;
if (callback.number === 0) {
// do stuff since all calls finished
callback.last();
}
}
callback.newQueue = function(num, last) {
callback.number = num;
callback.last = last;
}
// EXAMPLE USAGE
// our last function to be invoked
function afterEverythingDone(){ alert("done"); }
// create a new callback queue
callback.newQueue(3, afterEverythingDone);
// as time passes you call the callback
// function after every request
callback();
callback();
callback();
// after all call is finished
// afterEverythingDone() executes
Awesomeness again :)
One way is to use the same callback for both requests and proceed when both are complete:
var requestStatus = {
fooComplete: false,
barComplete: false
};
function callback(data) {
if (isFoo(data)) {
requestStatus.fooComplete = true;
} else if (isBar(data)) {
requestStatus.barComplete = true;
}
if (requestStatus.fooComplete && requestStatus.barComplete) {
proceed();
}
}
getAsync("foo", callback);
getAsync("bar", callback);
You'll probably want to flesh this out into a class.
Edit: added the async calls for clarity
You could have the callback function for each one indicate that their respective request has come back, and then execute the same common function. To illustrate:
var call1isBack = false;
var call2isBack = false;
function call1Callback() {
call1isBack = true;
commonCallback();
}
function call2Callback() {
call2isBack = true;
commonCallback();
}
function commonCallback() {
if (call1isBack && call2isBack) {
// do stuff, since you know both calls have come back.
}
}
Use a common callback handler with a counter that only allows passage into the "actual" processing section after the counter meets or exceeds the number of pending requests:
var commonHandler = (function() {
var counter=0, pendingCalls=2;
return function() {
if (++counter >= pendingCalls) {
// Do the actual thing...
}
}
})();
makeAjaxCall({args:args1, onComplete:commonHandler});
makeAjaxCall({args:args2, onComplete:commonHandler});
Using a closure around the anonymous function lets you avoid using a global variable for the counter.
Here's a snippet from a concurrent library I'm working on. All you need to do is instantiate a new Concurrent.Counter with the number of requests to await (before you execute them), and the callback to execute when they have finished. Before each of the asynchronous functions returns, have it call the decrement() method of the counter; once the counter has been decremented the number of times specified, the callback will be executed:
// Ensure the existence of the "Concurrent" namespace
var Concurrent = Concurrent || {};
/**
* Constructs a new Concurrent.Counter which executes a callback once a given number of threads have
* returned. Each Concurrent.Counter instance is designed to be used only once, and then disposed of,
* so a new one should be instantiated each additional time one is needed.
*
* #param {function} callback The callback to execute once all the threads have returned
* #param {number} count The number of threads to await termination before executing the callback
*/
Concurrent.Counter = function(callback, count) {
/**
* Decrements the thread count, and executes the callback once the count reaches zero.
*/
this.decrement = function() {
if (!(-- count)) {
callback();
}
};
};
// The number of asynchronous requests to execute
var requests = 10,
// Executes a callback once all the request tasks have returned
counter = new Concurrent.Counter(function() {
// this will be executed once the tasks have completed
}, requests),
// Tracks the number of requests made
i;
for (i = 0; i < requests; i ++) {
setTimeout(function() {
/*
* Perform an asynchronous task
*/
// Decrement the counter
counter.decrement();
}, 0);
}
This is written off the top of my head, but it should work.
function createCoordinator(onFinish) {
var count = 0;
return function (callback) {
count++;
return function () {
if (callback.apply(this, arguments))
count--;
if (count == 0)
onFinish();
}
}
}
var coordinate = createCoordinator(function () { alert('done!') });
// Assume sendAJAX = function (url, onreadystatechange)
sendAJAX('url1', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url1!');
return true;
}));
sendAJAX('url2', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url2!');
return true;
}));

Categories

Resources