Javascript long loops without workers - javascript

I require a long loop (long enough to make the browser hang if I used it naively). The contents of this loop requires other javascript files/libraries and I dont like the idea of concatonating them all into one file as this would make it harder to maintain. So (as far as I understand) this rules out web workers, Are there any other solutions out there?
I need the contents of the loop to run as fast as possible so any delay that can be measured in ms is unacceptable, any fixed delay at all would not be ideal.
I might be able to concatonate them all into a blob at runtime and then feed that into a web worker, I havn't really looked into this to far b/c it doesn't seem like a good solution.

You will have to use an asynchronous approach for this.
An example is to use a "dispatcher" function which invokes the work for each count and keep tracks of current count. It will make sure the next call is called asynchronous. When done the given callback is invoked.
Example
function startLoop(lFrom, lTo, workFunc, callback) {
var i = lFrom - 1;
(function loop() {
if (++i <= lTo) {
setTimeout(function() {
workFunc(i, loop); // start worker for this count
}, 9); // 9ms delay here - tweak as needed
}
else callback(); // done
})(); // self-invokes loop
}
Then in the worker function:
function worker(i, callback) {
// use i for what is needed, then
callback(); // will trigger next count
}
And of course, you can do batches instead of invoking a worker function for each count.
For block based approach for more "heavy" data, see also my answer here.
Simple demo below
startLoop(0, 20, worker, function() {alert("done")});
function startLoop(lFrom, lTo, workFunc, callback) {
var i = lFrom - 1;
(function loop() {
if (++i <= lTo) {
setTimeout(function() {
workFunc(i, loop); // start worker for this count
}, 9); // 9ms delay here - tweak as needed
}
else callback(); // done
})(); // self-invokes loop
}
function worker(i, callback) {
// use i for what is needed, then
document.querySelector("div").innerHTML += (i + "...");
callback(); // will trigger next count
}
<div></div>

Try to wrap loop body into setTimeout with zero time:
while (some_condition) {
setTimeout(function(){
// ... place your code here
}, 0);
}

Related

How to ensure blocking during a loop in Node.JS

Although I usually enjoy the callback-soup that is Node.JS, I found that a certain part of my code needs to be run in a blocking manner because of an SQLite issue. (Yes, I know I could try and address the SQLite part, it actually makes more sense to ensure blocking.)
I like using the async module, and though I have a feeling that module has a feature which can be used here, I can't seem to find it. Or, maybe there is a better module out there. Anyway, without further ado:
func = function(callback) {
let i = 0;
arr.forEach(val => {
if (val.trim().length > 0) {
console.log(`Starting for user ${val}.`);
let mylc = new lcapp(val);
////// TODO this needs to be made sycnronous. /////
async.series({
getMyDetails: callback => getMyDetails(mylc, callback)
}, (err, results) => handleResults(err, results, mylc, callback));
/////////////
}
});
};
The section of code surrounded by //// I would like to block until the handleResults function returns. I understand it will require reprogramming the callback in handleResults, or maybe I need to write a parent function around func but I'd like to see if StackOverflow people have some good ideas.
You could turn it into a function that recursively calls itself when the handleResults callback is hit.
You can do this by following the example below.
fun()
function fun() {
console.time("fun")
var arr = [1, 2, 3, 4, 5]
var i = arr.length - 1;
doStuff(doStuffCallback)
function doStuffCallback() {
if (i > 0) {
i--
doStuff(doStuffCallback)
} else {
console.timeEnd("fun")
}
}
function doStuff(callback) {
setTimeout(function() {
logIt()
callback()
}, 25)
}
function logIt() {
console.log(arr[i])
}
}
// Output:
// 5
// 4
// 3
// 2
// 1
// fun: about 160ms
PS: I'm assuming you only need to be synchronous within this method and the loop therein. Other code might still be running elsewhere in your application while this runs.
Yes, I know I could try and address the SQLite part, it actually makes more sense to ensure blocking.
No it doesn't, because you can't. You need to resolve whatever issue you have with it being async because there is no way to turn asynchronous code into synchronous code.

How to use setTimeout in a recursion to avoid browser from getting stuck on complicated processes

I have this code that uses an inefficientProcess() that consumes plenty of memory:
My goal is to use some sort of setTimeout(function(){...},0) technique so the browser will not get stuck while executing the code.
How do I change the code so it will work with setTimeout?
function powerOfTwo(num) {
inefficientProcess();
if (num > 0) {
return powerOfTwo(num-1)*2;
} else {
return 1;
}
}
function inefficientProcess() {
var sum;
for (var i=0; i < 500000; i++) {
sum+=10;
}
}
powerOfTwo(1000);
My goal is ofcourse to learn how to avoid browser crush when executing heavy calculations.
Javascript is single-threaded, and all your code is blocking.
There is a new standard in HTML5, WebWorkers API, that will allow you to delegate your task to a different thread. You can then pass a callback function to be executed with the result.
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
Simple example:
function powerOfTwo(num, callback) {
var worker = new Worker('inneficient.js');
worker.postMessage('runTask');
worker.onmessage = function(event) {
var num = event.data.result;
var pow;
if (num > 0) {
pow = Multiply(num-1)*2;
} else {
pow = 1;
}
callback(pow);
};
}
powerOfTwo(1000, function(pow){
console.log('the final result is ' + pow);
});
in inneficient.js you have something like:
//inneficient.js
function inefficientProcess() {
var sum;
for (var i=0; i < 500000; i++) {
sum+=10;
}
postMessage({ "result": sum});
}
inefficientProcess();
As was mentioned in Andre's answer, there's a new HTML5 standard that will allow you to set off a task on a different thread. Otherwise, you can call setTimeout with a time of 0 to allow the current execution path to finish (and perhaps some UI changes to render) before the inefficientProcess is called.
But whether you can use HTML5 or not, the powerOfTwo function has to be changed to be asynchronous - whoever calls it needs to provide a callback method that will be called when (a) a thread spun up via WebWorkers returns, or (b) the setTimeout method finishes.
Edited to add example:
function powerOfTwo(num, callback)
{
setTimeout(function ()
{
inefficientProcess();
if (num > 0)
callback(Multiply(num-1)*2);
else
callback(1);
}, 0);
}
function inefficientProcess() { ... }
The HTML element allows you to define when the JavaScript
code in your page should start executing. The “async” and “defer”
attributes were added to WebKit early September. Firefox has been
supporting them quite a while already.
Saw that on this Site

determining the end of asynchronous operations javascript

If I have a function that's passed this function:
function(work) {
work(10);
work(20);
work(30);
}
(There can be any number of work calls with any number in them.)
work performance some asynchronous activity—say, for this example, it just is a timeout. I have full control over what work does on the completion of this operation (and, in fact, its definition in general).
What's the best way of determining when all the calls to work are done?
My current method increments a counter when work is called and decrements it when it completes, and fires the all work done event when the counter is 0 (this is checked after every decrement). However, I worry that this could be a race condition of some sort. If that is not the case, do show my why and that would be a great answer.
There are a ton of ways you can write this program, but your simple technique of using a counter will work just fine.
The important thing to remember, the reason this will work, is because Javascript executes in a single thread. This is true of all browsers and node.js AFAIK.
Based on the thoughtful comments below, the solution works because the JS event loop will execute the functions in an order like:
function(work)
work(10)
counter++
Start async function
work(20)
counter++
Start async function
work(30)
counter++
Start async function
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
-- back out to event loop --
Async function completes
counter--
Counter is 0, so you fire your work done message
-- back out to event loop --
There's no race condition. There is the added requirement for every request made to perform a decrement when it's finished (always! including on http failure, which is easy to forget). But that can be handled in a more encapsulated way by wrapping you calls.
Untested, but this is the gist (I've implemented an object instead of a counter, so theoretically you can extend this to have more granular queries about specific requests):
var ajaxWrapper = (function() {
var id = 0, calls = {};
return {
makeRequest: function() {
$.post.apply($, arguments); // for example
calls[id] = true;
return id++;
},
finishRequest: function(id) {
delete calls[id];
},
isAllDone: function(){
var prop;
for(prop in calls) {
if(calls.hasOwnProperty(prop)) {return false;}
}
return true;
}
};
})();
Usage:
Instead of $.post("url", ... function(){ /*success*/ } ... ); We'll do
var requestId;
requestId = ajaxWrapper.makeRequest("url", ...
function(){ /*success*/ ajaxWrapper.finishRequest(requestId); } ... );
If you wanted to be even more sophisticated you could add the calls to finishRequest yourself inside the wrapper, so usage would be almost entirely transparent:
ajaxWrapper.makeRequest("url", ... function(){ /*success*/ } ... );
I have an after utility function.
var after = function _after(count, f) {
var c = 0, results = [];
return function _callback() {
switch (arguments.length) {
case 0: results.push(null); break;
case 1: results.push(arguments[0]); break;
default: results.push(Array.prototype.slice.call(arguments)); break;
}
if (++c === count) {
f.apply(this, results);
}
};
};
The following code below would just work. Because javascript is single threaded.
function doWork(work) {
work(10);
work(20);
work(30);
}
WorkHandler(doWork);
function WorkHandler(cb) {
var counter = 0,
finish;
cb(function _work(item) {
counter++;
// somethingAsync calls `finish` when it's finished
somethingAsync(item, function _cb() {
finish()
});
});
finish = after(counter, function() {
console.log('work finished');
});
};
I guess I should explain.
We pass the function that does work to the workhandler.
The work handler calls it and passes in work.
The function that does work calls work multiple times incrementing the counter
Since the function that does work is not asynchronous (very important) we can define the finish function after it has finished.
The asynchronouswork that is being done cannot finish (and call the undefined finish function) before the current synchronous block of work (the execution of the entire workhandler) has finished.
This means that after the entire workhandler has finished (and the variable finish is set) the asynchronous work jobs will start to end and call finish. Only once all of them have called finish will the callback send to after fire.

Execute the setInterval function without delay the first time

It's there a way to configure the setInterval method of javascript to execute the method immediately and then executes with the timer
It's simplest to just call the function yourself directly the first time:
foo();
setInterval(foo, delay);
However there are good reasons to avoid setInterval - in particular in some circumstances a whole load of setInterval events can arrive immediately after each other without any delay. Another reason is that if you want to stop the loop you have to explicitly call clearInterval which means you have to remember the handle returned from the original setInterval call.
So an alternative method is to have foo trigger itself for subsequent calls using setTimeout instead:
function foo() {
// do stuff
// ...
// and schedule a repeat
setTimeout(foo, delay);
}
// start the cycle
foo();
This guarantees that there is at least an interval of delay between calls. It also makes it easier to cancel the loop if required - you just don't call setTimeout when your loop termination condition is reached.
Better yet, you can wrap that all up in an immediately invoked function expression which creates the function, which then calls itself again as above, and automatically starts the loop:
(function foo() {
...
setTimeout(foo, delay);
})();
which defines the function and starts the cycle all in one go.
I'm not sure if I'm understanding you correctly, but you could easily do something like this:
setInterval(function hello() {
console.log('world');
return hello;
}(), 5000);
There's obviously any number of ways of doing this, but that's the most concise way I can think of.
I stumbled upon this question due to the same problem but none of the answers helps if you need to behave exactly like setInterval() but with the only difference that the function is called immediately at the beginning.
Here is my solution to this problem:
function setIntervalImmediately(func, interval) {
func();
return setInterval(func, interval);
}
The advantage of this solution:
existing code using setInterval can easily be adapted by substitution
works in strict mode
it works with existing named functions and closures
you can still use the return value and pass it to clearInterval() later
Example:
// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
console.log('hello');
}, 1000);
// clear interval after 4.5 seconds
setTimeout( _ => {
clearInterval(myInterval);
}, 4500);
To be cheeky, if you really need to use setInterval then you could also replace the original setInterval. Hence, no change of code required when adding this before your existing code:
var setIntervalOrig = setInterval;
setInterval = function(func, interval) {
func();
return setIntervalOrig(func, interval);
}
Still, all advantages as listed above apply here but no substitution is necessary.
You could wrap setInterval() in a function that provides that behavior:
function instantGratification( fn, delay ) {
fn();
setInterval( fn, delay );
}
...then use it like this:
instantGratification( function() {
console.log( 'invoked' );
}, 3000);
Here's a wrapper to pretty-fy it if you need it:
(function() {
var originalSetInterval = window.setInterval;
window.setInterval = function(fn, delay, runImmediately) {
if(runImmediately) fn();
return originalSetInterval(fn, delay);
};
})();
Set the third argument of setInterval to true and it'll run for the first time immediately after calling setInterval:
setInterval(function() { console.log("hello world"); }, 5000, true);
Or omit the third argument and it will retain its original behaviour:
setInterval(function() { console.log("hello world"); }, 5000);
Some browsers support additional arguments for setInterval which this wrapper doesn't take into account; I think these are rarely used, but keep that in mind if you do need them.
Here's a simple version for novices without all the messing around. It just declares the function, calls it, then starts the interval. That's it.
//Declare your function here
function My_Function(){
console.log("foo");
}
//Call the function first
My_Function();
//Set the interval
var interval = window.setInterval( My_Function, 500 );
There's a convenient npm package called firstInterval (full disclosure, it's mine).
Many of the examples here don't include parameter handling, and changing default behaviors of setInterval in any large project is evil. From the docs:
This pattern
setInterval(callback, 1000, p1, p2);
callback(p1, p2);
is identical to
firstInterval(callback, 1000, p1, p2);
If you're old school in the browser and don't want the dependency, it's an easy cut-and-paste from the code.
I will suggest calling the functions in the following sequence
var _timer = setInterval(foo, delay, params);
foo(params)
You can also pass the _timer to the foo, if you want to clearInterval(_timer) on a certain condition
var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);
For someone needs to bring the outer this inside as if it's an arrow function.
(function f() {
this.emit("...");
setTimeout(f.bind(this), 1000);
}).bind(this)();
If the above producing garbage bothers you, you can make a closure instead.
(that => {
(function f() {
that.emit("...");
setTimeout(f, 1000);
})();
})(this);
Or maybe consider using the #autobind decorator depending on your code.
You can set a very small initial delay-time (e.g. 100) and set it to your desired delay-time within the function:
var delay = 100;
function foo() {
console.log("Change initial delay-time to what you want.");
delay = 12000;
setTimeout(foo, delay);
}
To solve this problem , I run the function a first time after the page has loaded.
function foo(){ ... }
window.onload = function() {
foo();
};
window.setInterval(function()
{
foo();
}, 5000);
This example builds on #Alnitak's answer, but uses await Promise for finer granularity of control within the loop cycle.
Compare examples:
let stillGoing = true;
(function foo() {
console.log('The quick brown fox did its thing');
if (stillGoing) setTimeout(foo, 5000);
})();
foo();
In the above example we call foo() and then it calls itself every 5 seconds.
But if, at some point in the future, we set stillGoing to false in order to stop the loop, we'll still get an extra log line even after we've issued the stop order. This is because at any given time, before we set stillGoing to false the current iteration will have already created a timeout to call the next iteration.
If we instead use await Promise as the delay mechanism then we have an opportunity to stop the loop before calling the next iteration:
let stillGoing = true;
(async function foo() {
console.log('The quick brown fox did its thing');
await new Promise(resolve => setTimeout(resolve, 5000));
if (stillGoing) foo();
})();
foo();
In the second example we start by setting a 5000ms delay, after which we check the stillGoing value and decide whether calling another recursion is appropriate.
So if we set stillGoing to false at any point, there won't be that one extra log line printed after we set the value.
The caveat is this requires the function to be async, which may or may not be an option for a given use.
For Those using React, here is how I solve this problem:
const intervalRef = useRef(0);
useEffect(() => {
if (condition is true){
if (intervalRef.current === 0) {
callMyFunction();
}
const interval = setInterval(() => {
callMyFunction();
}, 5_000);
intervalRef.current = interval;
} else {
clearInterval(intervalRef.current);
}
}, [deps]);
// YCombinator
function anonymous(fnc) {
return function() {
fnc.apply(fnc, arguments);
return fnc;
}
}
// Invoking the first time:
setInterval(anonymous(function() {
console.log("bar");
})(), 4000);
// Not invoking the first time:
setInterval(anonymous(function() {
console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
console.log("baz");
}, 4000);
Ok this is so complex, so, let me put it more simple:
function hello(status ) {
console.log('world', ++status.count);
return status;
}
setInterval(hello, 5 * 1000, hello({ count: 0 }));
If you can use RxJS, there is something called timer():
import { Subscription, timer } from 'rxjs';
const INITIAL_DELAY = 1;
const INTERVAL_DELAY = 10000;
const timerSubscription = timer(INITIAL_DELAY, INTERVAL_DELAY)
.subscribe(() => {
this.updateSomething();
});
// when destroying
timerSubscription.unsubscribe();
With ES2017, it may be preferable to avoid setInterval altogether.
The following solution has a much cleaner execution flow, prevents issues if the function takes longer than the desired time to complete, and allows for asynchronous operations.
const timeout = (delayMs) => new Promise((res, _rej) => setTimeout(res, delayMs));
const DELAY = 1_000;
(async () => {
while (true) {
let start_time = Date.now();
// insert code here...
let end_time = Date.now();
await timeout(DELAY - (end_time - start_time));
}
})();
There's a problem with immediate asynchronous call of your function, because standard setTimeout/setInterval has a minimal timeout about several milliseconds even if you directly set it to 0. It caused by a browser specific work.
An example of code with a REAL zero delay wich works in Chrome, Safari, Opera
function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}
You can find more information here
And after the first manual call you can create an interval with your function.
actually the quickest is to do
interval = setInterval(myFunction(),45000)
this will call myfunction, and then will do it agaian every 45 seconds which is different than doing
interval = setInterval(myfunction, 45000)
which won't call it, but schedule it only

How can I show a list of every thread running spawned by setTimeout/setInterval

I want to do this either by pure javascript or any sort of console in a browser or whatever.
Is it possible?
Thanks
Further explanations:
I want to debug a library that does animations. I want to know if there's multiple timers created if there are multiple objects being animated.
Note that setTimeout() does not spawn new threads. Browser side scripting is not only single threaded, but the JavaScript evaluation shares the same single thread with the page rendering (Web Workers apart).
Further reading:
How JavaScript Timers Work by John Resig
You may want to build a timer manager yourself:
var timerManager = (function () {
var timers = [];
return {
addTimer: function (callback, timeout) {
var timer, that = this;
timer = setTimeout(function () {
that.removeTimer(timer);
callback();
}, timeout);
timers.push(timer);
return timer;
},
removeTimer: function (timer) {
clearTimeout(timer);
timers.splice(timers.indexOf(timer), 1);
},
getTimers: function () {
return timers;
}
};
})();
Then use it as follows:
var t1 = timerManager.addTimer(function () {
console.log('Timer t1 triggered after 1 second');
}, 1000);
var t2 = timerManager.addTimer(function () {
console.log('Timer t2 triggered after 5 second');
console.log('Number of Timers at End: ' + timerManager.getTimers().length);
}, 5000);
console.log('Number of Timers at Start: ' + timerManager.getTimers().length);
The above will display the following result in the console:
// Number of Timers at Start: 2
// Timer t1 triggered after 1 second
// Timer t2 triggered after 5 second
// Number of Timers at End: 0
Note that the timerManager implementation above uses the Array.indexOf() method. This has been added in JavaScript 1.6 and therefore not implemented by all browsers. However, you can easily add the method yourself by adding the implementation from this Mozilla Dev Center article.
Finally done, it was interesting for me so I spent some time trying to come up with something, and here it's
It overrides browser's setTimeout and fill active status of current active calls in window._activeSetTimeouts hash, with window._showCurrentSetTimeouts() demo function that displays current setTimeout calls that are waiting.
if(typeof window._setTimeout =='undefined') {
window._setTimeout=window.setTimeout;
window._activeSetTimeouts={};
window._activeSetTimeoutsTotal=0;
window._setTimeoutCounter=0;
window._showCurrentSetTimeouts=function() {
var tgt=document.getElementById('_settimtouts');
if(!tgt) {
tgt=document.createElement('UL');
tgt.style.position='absolute';
tgt.style.border='1px solid #999';
tgt.style.background='#EEE';
tgt.style.width='90%';
tgt.style.height='500px';
tgt.style.overflow='auto';
tgt.id='_settimtouts';
document.body.appendChild(tgt);
}
tgt.innerHTML='';
var counter=0;
for(var i in window._activeSetTimeouts) {
var li=document.createElement('LI');
li.innerHTML='[{status}] {delay} ({calltime})<br /><pre style="width: 100%; height: 5em; overflow: auto; background: {bgcolor}">{cb}</pre>'.f(window._activeSetTimeouts[i]);
li.style.background=(counter++%2)?'#CCC' : '#EEB';
tgt.appendChild(li);
}
}
window.setTimeout=function(cb, delay) {
var id = window._setTimeoutCounter++;
var handleId = window._setTimeout(function() {
window._activeSetTimeouts[id].status='exec';
cb();
delete window._activeSetTimeouts[id];
window._activeSetTimeoutsTotal--;
}, delay);
window._activeSetTimeouts[id]={
calltime:new Date(),
delay:delay,
cb:cb,
status:'wait'
};
window._activeSetTimeoutsTotal++;
return id;
}
//the following function is for easy formatting
String.prototype.f=function(obj) {
var newStr=this+'';
if(arguments.length==1) {
if(typeof(obj)=='string') {
obj={x:obj};
}
for(var i in obj) {
newStr=newStr.replace(new RegExp('{'+i+'}', 'g'), obj[i]+'');
}
newStr+='';
} else {
for(var i=0; i<arguments.length; i++) {
newStr=newStr.replace('{'+(i+1)+'}', arguments[i]);
}
}
return newStr;
}
}
//following line for test
for(var i=0; i<5; i++) setTimeout(window._showCurrentSetTimeouts, 3000*i);
As others have mentioned, setTimeout doesn’t spawn a thread. If you want a list of all the timeout ids (so you can cancel them, for example) then see below:
I don’t think you can get a list of all timeout ids without changing the code when they are called. setTimeout returns an id—and if you ignore it, then it's inaccessible to your JavaScript. (Obviously the interpreter has access to it, but your code doesn't.)
If you could change the code you could do this:
var timeoutId = [];
timeoutId.push(setTimeout(myfunc, 100));
…Making sure that timeoutId is declared in global scope (perhaps by using window.timeoutId = []).
Just off the top of my head, but to reimplement setTimeout you’d have to do something like this:
var oldSetTimeout = setTimeout;
setTimeout = function (func, delay) {
timeoutId.push(oldSetTimeout(func, delay));
}
This isn’t tested, but it gives you a starting point. Good idea, molf!
Edit: aularon's answer gives a much more thorough implementation of the above idea.

Categories