I have a fairly long running (3 to 10 second) function that loads data in the background for a fairly infrequently used part of the page. The question I have is what is the optimal running time per execution and delay time between to ensure that the rest of the page stays fairly interactive, but the loading of the data is not overly delayed by breaking it up?
For example:
var i = 0;
var chunkSize = 10;
var timeout = 1;
var data; //some array
var bigFunction = function() {
var nextStop = chunkSize + i; //find next stop
if (nextStop > data.length) { nextStop = data.length }
for (; i < nextStop; i++) {
doSomethingWithData(data[i]);
}
if (i == data.length) { setTimeout(finalizingFunction, timeout); }
else { setTimeout(bigFunction , timeoutLengthInMs); }
};
bigFunction(); //start it all off
Now, I've done some testing, and a chunkSize that yields about a 100ms execution time and a 1ms timeout seem to yield a pretty responsive UI, but some examples I've seen recommend much larger chunks (~300ms) and longer timeouts (20 to 100 ms). Am I missing some dangers in cutting my function into too many small chunks, or is trial and error a safe way to determine these numbers?
Any timeout value less than roughly 15ms is equivalent - the browser will update the DOM, etc and then execute the timeout. See this and this for more info. I tend to use setTimeout(fn, 0).
I would check the time elapsed instead of guessing numbers, because as Jason pointed out, there will be speed differences between clients:
var data; // some array
var i = 0;
var MAX_ITERS = 20; // in case the system time gets set backwards, etc
var CHECK_TIME_EVERY_N_ITERS = 3; // so that we don't check elapsed time excessively
var TIMEOUT_EVERY_X_MS = 300;
var bigFunction = function () {
var maxI = i + MAX_ITERS;
if (maxI > data.length) { maxI = data.length }
var nextTimeI;
var startTime = (new Date()).getTime(); // ms since 1970
var msElapsed;
while (i < maxI) {
nextTimeI = i + CHECK_TIME_EVERY_N_ITERS;
if (nextTimeI > data.length) { nextTimeI = data.length }
for (; i < nextTimeI; i++) {
doSomethingWithData(data[i]);
}
msElapsed = (new Date()).getTime() - startTime;
if (msElapsed > TIMEOUT_EVERY_X_MS) {
break;
}
}
if (i == data.length) {
setTimeout(finalizingFunction, 0);
} else {
setTimeout(bigFunction , 0);
}
};
bigFunction(); //start it all off
The 1ms timeout is not actually 1 ms. By the time the thread yields, to achieve the 1ms, it probably yielded much more - because the typical thread time-slice is 30ms. Any number of other threads may have executed during the 1ms timeout, which could mean that 200ms went by before you received control again.
Why not just have the 'doSomethingWithData' execute in an entirely different thread?
Related
I've been bashing my head against this wall I am completely new to JavaScript coming from c#
and I am completely baffled for my class I have to smooth out a simple code we made to count down from zero by making it into a loop and for the life of me I just cant get it to work
var i = 10;
var timeout = 10000;
var x = 10
if (i == 5) {
alert("help me")
}
while (i > 0) {
//10
setTimeout(() => {
document.getElementById("counter").innerHTML = i;
i = i - 1;
}, timeout);
timeout = timeout - 1000;
}
Asynchrony in JavaScript is a rather involved subject.
Traditional loops (for, while, do..while, for..of) are synchronous, and so cannot help you reach your goal here.
To do what you want, you can use indirect recursion like so:
const countdown = (from = 10, to = 0, interval = 1000, cb = console.log) => {
if(from < 0) return
cb(from)
setTimeout(() =>
countdown(--from), interval)
}
countdown()
There is also a more modern approach that enables the use of syntax that looks a bit more familiar. This approach uses for await... of, the syntax for which does not appear to be supported by StackOverflow's transpiler:
const delay = (interval) => new Promise((resolve) =>
setTimeout(resolve, interval))
async function* countdown(from = 10, to = 0, interval = 1000) {
for(;from >= 0; from--) {
yield from
await delay(interval)
}
}
for await(let count of countdown()) {
console.log(count)
}
setInterval is what you're looking for to execute a function every certain amount of time:
let i = 10;
let interval = setInterval(function() {
document.getElementById("counter").innerHTML = i;
i--;
if(i < 0) clearInterval(interval);//Clear the interval when complete
}, 1000);//Every 1000 ms = every second
The code below will countdown to zero.
var i = 10;
while (i > 0){
i--;
console.log(i);
}
I'm working on a very performance critical part of a browser game and was just splitting apart a big pile of code into more manageable chunks but it seems that I'm paying a pretty serious (~40% in total) performance loss for these extra function calls.
At first I figured that V8 just doesn't do inlining upon compilation but then I tried out this little test:
const nn = 1000000000;
(()=>{
var t = Date.now();
var total = 0;
for (var i = 0; i < nn; i++) {
total += i;
}
console.log(total, Date.now() - t);
})();
(()=>{
var t = Date.now();
var total = 0;
function useless() {}
for (var i = 0; i < nn; i++) {
total += i;
useless();useless();useless();
}
console.log(total, Date.now() - t);
})();
(new class {
useless() {}
test() {
var t = Date.now();
var total = 0;
for (var i = 0; i < nn; i++) {
total += i;
this.useless();this.useless();this.useless();
}
console.log(total, Date.now() - t);
}
}).test();
And in each case, I get an identical result, useless function calls or not. That tells me that there is practically no cost for a useless function call.
Yet in my real code, adding useless function calls incurs a very real penalty for each added, for example in
add_expected_length(v) {
this.set_block_raw(); // <- this is the useless one
while (v > 127) { this.buffer.push((v & 127) + 128); v = Math.trunc(v / 128); }
this.buffer.push(v);
}
even if set_block_raw method is empty, adding it there makes the whole algorithm 5% slower, add two in a row and its 7%, three is 9% and on and on, it seems to scale almost linearly with each useless call added I get a 1-2% performance decrease.
Now if I break my class apart and start examining individual calls, isolating pieces of code here and there the problem goes away, the useless call is ignored but trying to figure out what is wrong in this huge inter-dependent algorithm like that would just take forever.
This seems very bizarre to me and I really want to dig into what V8 generates to see what is causing this. Is there a way to peer behind the js and see what chrome and V8 actually does with it?
I've got multiple long running tasks, as in longer than ~10ms, that impact the responsiveness of the browser. The worst ones, such as loading and parsing 3D models from files, are already offloaded to Web Workers so that they won't affect the render loop.
Some tasks, however, aren't easily ported to Workers and therefore have to be distributed over multiple frames in the main thread. Instead of doing a 1 second task in one go, I'd like to split it into ~5ms packages to give the browser the chance to execute other events (mouse move, requestAnimationFrame, ...) in between.
Generator functions, in combination with setTimeout, seem to be the easiest way to do that. I've hacked something together that does the job but I'm wondering if there is a nicer/cleaner way to solve this issue.
The code below computes the mean of 100 million invocations of Math.random().
The first version computes the mean in one go, but stalls the browser for ~1.3 seconds.
The second version abuses generator functions to yield after every 5 million points, thereby giving the browser the chance to execute other events (mouse move) in between. The generator function is repeatedly called through a setTimout loop, until it has processed all 100 million samples.
<html>
<head></head>
<body>
<script>
let samples = 100 * 1000 * 1000;
{ // run complete task at once, possibly stalling the browser
function run(){
let start = performance.now();
let sum = 0.0;
for(let i = 0; i < samples; i++){
sum = sum + Math.random();
}
let mean = sum / samples;
let duration = performance.now() - start;
console.log(`single-run: duration: ${duration}`);
console.log(`single-run: sum: ${sum}`);
console.log(`single-run: mean: ${mean}`);
}
run();
}
{ // run in smaller packages to keep browser responsive
// move mouse to check if this callback is executed in between
document.body.addEventListener("mousemove", () => {
console.log("mouse moved");
});
function * distributedRun(){
let start = performance.now();
let packageSize = 5 * 1000 * 1000;
let sum = 0.0;
for(let i = 0; i < samples; i++){
sum = sum + Math.random();
if((i % packageSize) === 0){
yield sum;
}
}
let mean = sum / samples;
let duration = performance.now() - start;
console.log(`distributed-run: duration: ${duration}`);
console.log(`distributed-run: sum: ${sum}`);
console.log(`distributed-run: mean: ${mean}`);
yield sum;
}
let generatorInstance = distributedRun();
function loop(){
let result = generatorInstance.next();
console.log(`distributed-run intermediate result: ${result.value}`);
if(!result.done){
setTimeout(loop, 0);
}
}
loop();
}
</script>
</body>
</html>
ES2018 has async iterators which kind of sound like what I'm looking for but I'm not sure if they're really meant for this kind of problem. Using it like this still stalls the browser:
for await (const result of distributedRun()) {
...
}
(Tried some async's here and there and at the runDistributed() function but tbh, I'm still learning the details of await/async)
Here is a slightly modified version of your code. If you ajust chunk depending on your computation complexity and the amount of lag you can allow, it should work fine.
let samples = 100 * 1000 * 1000;
let chunk = 100000;
async function run() {
let sum = 0.0;
for(let i=0; i<samples; i++) {
sum += Math.random();
if (i % chunk === 0) {
console.log("finished chunk")
// wait for the next tick
await new Promise(res => setTimeout(res, 0));
}
}
let mean = sum / samples;
console.log("finished computation", mean);
}
setTimeout(run, 0);
setTimeout(func,delay) seems to fire very precisely at the specified time, as long as the page is not running some script while it is trying to fire the function. But is there a way to take lag into account?
For example if I set a 3sec timeout and javascript runs some heavy code which makes the page leggy for a while. As long as processing the "heavy code" is done by the 3sec it will timeout after ~3sec.
Is there a way to take the "heavy code" processing time into account and timeout after 3sec + the time the page was blocked?
Here is a jsfiddle: http://jsfiddle.net/me2loveit2/mCj2J/
var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100
function test() {
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}
function block() {
for (var i = 0; i < 100000000; i++) {};
}
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
As #adeneo pointed out, there is no such possibility. You simply can't know how effectively processor is running your code at the other end, or the tasks it is currently making which might slow it down further. Every case is different. setTimeout tries to match the specified time but very often, it just can't be exact.
I think the solution is just to change your mindset. Try to avoid long blocking synchronous operations such as for (var i = 0; i < 10000000; i++) {}; When you drop or modify these you can have more accurate setTimeout firing. The reason being, that there will be smaller executable chunks in the event queue.
Generally speaking, there are different ways to do processing of blocking events. For instance, you could look into Web workers or yielding setTimeout calls. (See links at the end of this post).
Hence, I don't know your specific case, but if you are trying to make many setTimeout calls just as in game programming (loops) solution is to try to alter future setTimeout calls to contain smaller value so the full loop will try to catch up the simulation to match the specific frame rate.
This is usually done with combination of requestAnimationFrame.
Short example of a loop which attemps to run 30 fps in the browser:
You can also view it in js fiddle
/**
* This is example to run loop with 30fps in the browser
*
*/
var gl = {
now: new Date().getTime(),
dt: 0.0,
last: new Date().getTime(),
// physics with 0.033333 steps
step: 1 / 30
},
frames = 0,
started = new Date().getTime();
/**
* Game loop
*
*/
var gameLoop = function () {
gl.now = new Date().getTime();
gl.dt = gl.dt + Math.min(1, (gl.now - gl.last) / 1000);
while (gl.dt > gl.step) {
gl.dt = gl.dt - gl.step;
// Increase frames
frames++;
if(frames === 30) {
// How long it took to execute 30 frames in 1000 ms ?
document.body.innerHTML = "We executed 30 frames in " + (new Date().getTime() - started) + " ms.";
started = new Date().getTime();
frames = 0;
}
}
// last
gl.last = gl.now;
// next
requestAnimationFrame(gameLoop);
};
// Start the game loop
gameLoop();
Hopefully, this gave you some ideas. Thus, don't forget to use css transitions and similar when those can be applied.
For further reading, I recommend:
Yielding setTimeout calls
How to avoid blocking the browser while doing heavy work
Using Web Workers
Cheers.
Not sure I understand the question 100%, but if you do something like this, you're able to see if the other stuff (heavy processing) is not done by the time the timeout runs. This should take about 5 seconds, if you switch the 2000000000 to a 20000 (less proccessing), it should come back at 3 seconds.
var timeStart = new Date().getTime();
setTimeout(test, 3000); //<-- timeout should be 100
var currentTime = new Date().getTime();
function test() {
if (currentTime - timeStart < 3000){
var timeAfter100MS = new Date().getTime();
$('body').append('Took less than 3 seconds - ' + (timeAfter100MS - timeStart)+"ms");
}else{
$('body').append('Took more than 3 seconds');
}
}
function block() {
for (var i = 0; i < 10000000; i++) {};
currentTime = new Date().getTime();
}
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
If the 'important' code causes really significant lag, and precise timing is important, you can keep the precision by using two timeouts. The first timeout measures the lag and sets the second timeout accordingly.
Here's an example, using your code as a basis:
var timeStart = new Date().getTime();
var msDelay = 3000;
setTimeout(testLag, msDelay - 500);
function testLag() {
var timeTestLag = new Date().getTime();
$('body').append('testLag() fired at: ' + (timeTestLag - timeStart) + 'ms<br/>');
setTimeout(test, timeStart + msDelay - timeTestLag);
}
function test() {
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000, but it did not take the blocked time into account.)');
}
function block() {
for (var i = 0; i < 1000000000; i++) {};
}
block();
block();
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
Note that the block is significantly more intensive than yours - I added a zero to your 100000000, and added a couple of extra block() calls. You might need to adjust the figures to get a sensible level of block for your own machine.
Based on Mauno's Answer I came up with a solution to temporarily "track the lag" using an interval. I am setting an interval with short intervals to capture the delay and set another timeout if necessary. Here is the working example: http://jsfiddle.net/me2loveit2/mCj2J/14/
It is approximate, but always walls within 100ms of the target which is good enough for me. It could be even more accurate if I increase the interval rate, but what I got is good enough for me.
I know using timeout & interval is not the best but sometimes the only way. I am just using them for a couple of seconds on page load and that's it.
Here is the code:
var timeStart = new Date().getTime();
var aditionalTimeout = 0;
var myTimeout;
setTimer(3000);
block();
block();
block();
var timeEnd = new Date().getTime();
$('body').append('Page was blocked(running importaint code :)) for:<br>' + (timeEnd - timeStart) + 'ms<br>');
function setTimer(milliseconds) {
//allow additional time to account for the huge lag the page has on load
recoverLagTime(milliseconds);
myTimeout = setTimeout(function () {
if (!aditionalTimeout) {
test();
} else {
if (aditionalTimeout >= milliseconds) {
test();
return;
}
setTimer(aditionalTimeout);
}
}, milliseconds);
}
function recoverLagTime(timeoutTime) {
aditionalTimeout = 0;
var interval = 50;
var counter = Math.ceil(timeoutTime / interval);
var startTime = new Date().getTime();
var intervalTime;
var lagInterval = setInterval(adjustTimer, interval);
function adjustTimer() {
if (counter <= 0 || aditionalTimeout < 0) {
clearInterval(lagInterval);
return;
}
counter--;
intervalTime = new Date().getTime();
var diff = (intervalTime - startTime);
if (diff > (interval + 5)) {
aditionalTimeout += (diff - interval);
}
startTime = new Date().getTime();
}
}
function test() {
aditionalTimeout = -100;//stop the check function
var timeAfter100MS = new Date().getTime();
$('body').append('Timeout Fired at: <br>' + (timeAfter100MS - timeStart) + 'ms<br> (should be ~3000 + ~amount blocked)');
}
function block() {
for (var i = 0; i < 100000000; i++) {};
}
I wrote a small (2-file) library for exactly these purposes : running heavy code whenever the CPU has idle time (using requestAnimationFrame) by splitting the code into smaller iterations so it doesn't block the whole application by allotting a specific percentage of CPU time to execution the code, and use the remainder to execute other scripts / update UI.
It functions similarly to other answers, but might be convenient to you as you can easily calculate elapsed time between executions, if you need to know these figures (or use it to leverage operations within your application, as that's what it was written for)
https://github.com/igorski/zThreader
this file
http://www.iguanademos.com/Jare/docs/html5/Lessons/Lesson2/js/GameLoopManager.js
taken from this site
Here is the code:
// ----------------------------------------
// GameLoopManager
// By Javier Arevalo
var GameLoopManager = new function() {
this.lastTime = 0;
this.gameTick = null;
this.prevElapsed = 0;
this.prevElapsed2 = 0;
I understand the declaration of variables,
and they are used to record the time between frames.
this.run = function(gameTick) {
var prevTick = this.gameTick;
this.gameTick = gameTick;
if (this.lastTime == 0)
{
// Once started, the loop never stops.
// But this function is called to change tick functions.
// Avoid requesting multiple frames per frame.
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
this.lastTime = 0;
}
}
I don't understand why he uses var bindThis = this
this.stop = function() {
this.run(null);
}
This function set's gameTick to null, breaking the loop in this.tick function.
this.tick = function () {
if (this.gameTick != null)
{
var bindThis = this;
requestAnimationFrame(function() { bindThis.tick(); } );
}
else
{
this.lastTime = 0;
return;
}
var timeNow = Date.now();
var elapsed = timeNow - this.lastTime;
if (elapsed > 0)
{
if (this.lastTime != 0)
{
if (elapsed > 1000) // Cap max elapsed time to 1 second to avoid death spiral
elapsed = 1000;
// Hackish fps smoothing
var smoothElapsed = (elapsed + this.prevElapsed + this.prevElapsed2)/3;
this.gameTick(0.001*smoothElapsed);
this.prevElapsed2 = this.prevElapsed;
this.prevElapsed = elapsed;
}
this.lastTime = timeNow;
}
}
}
Most of this code is what I don't understand, I can see he is recording the time elapsed between frames, but the rest of the code is lost to me.
On the website he uses the term singleton, which is used to prevent the program trying to update the same frame twice?
I have a bit of experience with the javascript syntax, but the concepts of singleton, and the general goal/function of this file is lost to me.
Why is the above code needed instead of just calling
requestAnimationFrame(function() {} );
The reason he uses bindThis is that he is passing a method into an anonymous function on the next line. If he merely used this.tick(), this would be defined as the context of requestAnimationFrame. He could achieve the same thing by using call or apply.
Singletons are classes that are only instantiated once. This is a matter of practice, and not a matter of syntax - javascript doesn't know what a singleton is. By calling it a "Singleton", he is merely communicating that this is a class that is instantiated only once, and everything that needs it will reference the same instance.