I have a UI where I need animations to run smoothly. Every so often, I need to do a semi-large data calculation that makes the animation skip until it is this calculation is completed.
I am trying to get around this by making the data calculation async with setTimeout. Something like setTimeout(calcData(), 0);
The whole code is something like this (simplified):
while (animating) {
performAnimation();
if (needCalc) {
setTimeout(calcData(), 0);
}
}
But I still get a skip in the animation. It runs smoothly when I do not need to do any data calculations. How can I do this effectively? Thanks!
You are seeing the skip because only one javascript thread is run at once. When something is done asynchronously the javascript engine puts it a queue to be ran later, then finds something else to execute. When something in the queue needs to be done the engine will pull it out and execute it, blocking all other operations until it completes.The engine then pulls something else out of its queue to execute.
So if you want to allow your render to run smoothly you must break up your calculation into multiple async calls, allowing the engine to schedule the render operation in between calculations. This is easy to accomplish if you are just iterating over a array, so you can do something like:
var now=Date.now;
if(window.performance&&performance.now){//use performace.now if we can
now=performance.now;
}
function calculate(){
var batchSize=10;//If you have a exceptionally long operation you may want to make this lower.
var i=0;
var next=function(){
var start=now();
while(now()-start<14){//14ms / frame
var end=Math.min(i+batchSize,data.length);
for(;i<end;i++){//do batches to reduce time overhead
do_calc(data[i]);
}
}
if(i<data.length) setTimeout(next,1)//defer to next tick
};
next();
}
calculate();
function render(){
do_render_stuff();
if(animating) {
requestAnimationFrame(render);//use requestAnimationFrame rather then setTimeout for rendering
}
}
render();
Better yet, if you can, you should use WebWorkers which work in a different thread, completely separate from the main js engine. However you are stuck with this if you need to do something you cant do in a WebWorker, such as manipulating the DOM tree.
Firstly, let's talk what's going on in your code:
while (animating) {
performAnimation();
if (needCalc) {
// it should be setTimeout(calcData, 0);
setTimeout(calcData(), 0);
}
}
In line setTimeout(calcData(), 0); really you don't defer calling of calcData function, you call it, because you use () operator after function name.
Secondly, lets think, what's going on when you really make defer calling for calcData in the code above: commonly JavaScript is running in one thread, so, if you have code like this:
setTimeout(doSomething, 0);
while (true) {};
doSomething will never be called, because interpreter of javascript executes while loop forever and it hasn't "free time" to execute other things (even UI) . setTimeout - just say to schedule execution of doSomething when interpreter will be free and it's time to execute this function.
So, when browser executes javascript function, all other stuff become freezing.
Solution:
If you have big data that you need to process, maybe it would be better to make calculations on backend and after send results to frontend.
Usually when you need to make some calculation and render results it's better to use requestAnimationFrame than while loop. Browser will execute function passed in requestAnimationFrame as soon as possible, but also you give browser a time to handle other events. You can see smooth redrawing using requestAnimationFrame for game (step-by-step tutorial here).
If you really want to process huge amount of data at frontend part and you want to make ui works smooth, you can try to use WebWorkers. WebWorkers look like threads in JavaScript, you need to communicate between main UI "thread" and WebWorker by passing messages from one to another and back and calculations on WebWorker don't affect UI thread.
Mostly, your problem boils down to your incorrect usage of setTimeout()
setTimeout(calcData(), 0);
The first argument to setTimeout is a REFERENCE to the function that you wish to call. In your code, you are not referencing the calcData function, you are invoking it because you've included () after the function name.
Second, the fact that you've put 0 for the delay does not mean you will have a 0 second delay before the function runs. JavaScript runs in a single threaded context. The setTimeout function is placed in a queue and executed when the JavaScript engine is available, but no sooner than a minimum of 10ms or the amount you specify (whichever is less).
Realistically, your line should be:
setTimeout(calcData(),10);
Related
this is not a common condition, but what's the misuse of it?
var t = true;
setTimeout(function(){
t=false;
},1000);
while(t){
//loop...
//if t==false, break loop.
}
another condition, it causes endless loop too:
button.onlcick = function(){
t = false;
}
JavaScript is single-threaded; setTimeout callback won't be called when you're blocking the main event loop with that while (while (t) { ... }).
If you're running your code in browser, you can't really do anything about it but writing your code in other way;
Instead of blocking the main event loop, you can use Promises Pattern.
If you're running your code in something like node you can use native modules, making you able to create threads (like threads_a_gogo.)
Because the while loop never exits, your other code is never run when something is done synchronously. My best offer for you would be not to use a while loop and instead have a recurring event such as setTimeout and make the timeout run itself when complete.
This way you're not creating a closed environment.
It won't work because javascript is not multithreaded - until your current thread of execution ends (and it won't as long as you're running your while loop), no other code is executed (no timeout call) and the UI is frozen (button clicks will not respond).
There might be a way to do something like that with the new Web Workers feature in html5, but as of now i'm not able to tell with certainty.
you can use labels with break condition like
outer:while()
{
//code
if()
break outer;
}
I am not quite sure what the technical term for this is. I have a GUI with interactive graphics. After the user has interacted with the GUI, I need to perform some CPU intensive action. However, user input is very frequent, so I only want to call the function after e.g. 1000ms of no userinput. Below the pattern that I use:
scheduler = (function(){
var timer;
function exec(call, delay){
clearTimeout(timer);
timer = setTimeout(call, delay);
};
return exec;
})()
I.e. if the 3 calls to scheduler are done right after each other, only the final one will actually be executed:
scheduler(function(){alert('foo')}, 1000);
scheduler(function(){alert('bar')}, 1000);
scheduler(function(){alert('zoo')}, 1000);
It seems to work, but it feels a bit hacky I am a little worried about any caveats of Javascript setTimeout, especially the scoping problems. Does this seem like a reliable pattern I could use on a larger scale? Will the inline function that I pass to scheduler be able to lookup all objects in its lexical scope as usual, when it is called by settimeout? What about if I have several of these scheduler instances? Could they interfere with each other? Is there an alternative way of accomplishing this?
You could opt for using web worker threads instead:
https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
http://www.html5rocks.com/en/tutorials/workers/basics/
What I would do:
http://jsfiddle.net/gunderson/4XXQ4/1/
var severQueue = [];
var delay;
$("#inputSquare").mousemove(onMouseMove);
function onMouseMove(){
if (delay){
clearTimeout(delay);
}
serverQueue.push("doSomething")
delay = setTimeout(sendToServer, 1000);
}
function sendToServer(){
console.log(serverQueue.length);
delay = null;
$("#inputSquare").addClass("activated");
// do some ajax using serverQueue
// we'll just simulate it with another timeout
for (var i in serverQueue){
serverQueue.pop();
}
onComplete = setTimeout(onAjaxComplete, 1000);
}
function onAjaxComplete(){
$("#inputSquare").removeClass("activated");
}
In theory, your solution looks like it will work. There are no scoping problems related to you passing a callback function to your scheduler function; the callback will close over whatever environment it was created in, just like any other function in JavaScript. That being said, scoping rules can be a bit tricky in JavaScript, so make sure that you read up on it.
In practice, there may be some browser-specific issues related to setTimeout that may make this solution unworkable. For example, the frequency at which certain browsers execute setTimeout callbacks may vary such that you'll be waiting longer than you expect for a callback to be executed. All setTimeout callbacks will be executed sequentially; they'll never be executed in parallel. However, you have guarantees as to what order they will be executed in.
All that being said, any major gotcha in your solution will likely have more to do with the callbacks that your registering rather than the way in which you're registering them.
The debounce function in underscore.js does exactly this:
debounce _.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function that will postpone its execution until after wait milliseconds have
elapsed since the last time it was invoked. Useful for implementing
behavior that should only happen after the input has stopped arriving.
For example: rendering a preview of a Markdown comment, recalculating
a layout after the window has stopped being resized, and so on.
This question already has answers here:
Why is setTimeout(fn, 0) sometimes useful?
(19 answers)
Closed 9 years ago.
I have problem when using jQuery plugin tablesorter and I can't call trigger twice.
For example this won't work:
this._$table.trigger('update');
this._$table.trigger('sorton', [[[1,1]]]);
But this works:
this._$table.trigger('update');
setTimeout($.proxy(function() {
this._$table.trigger('sorton', [[[1,1]]]);
}, this), 1);
And then I see that problem was in trigger 'update', it call method with body:
function () {
var me = this;
setTimeout(function () {
// rebuild parsers.
me.config.parsers = buildParserCache(
me, $headers);
// rebuild the cache map
cache = buildCache(me);
}, 1);
}
Why did the tablesorter developer use setTimeout with one millisecond?
Short asnwer: Function execution queueing
This is the short answer to your question. setTimeout with either 0 or 1 millisecond is used for function execution queueing. Read on to find out why and how.
Javascript has single threaded execution
Javascript engine is a single threaded process. So whenever developers wanted to defer some function execution to get executed right after the current one that's just being executed, a setTimeout is being used to actually queue the next function... It doesn't have anything to do directly with events although functions may be event handlers. The only event in this equation is the timeout event that setTimeout creates.
This is an example of two functions where the first function during its execution queues a second function to be executed right after it.
function first()
{
// does whatever it needs to
// something else needs to be executed right afterwards
setTimeout(second, 1);
// do some final processing and exit
return;
}
function second()
{
// whatever needs to be done
}
So to javascript engine thread the execution queue looks like this:
first()
second()
Mind that this has nothing to do with function call stack.
Why 1ms?
1ms is a very short amount of time, which (almost) assures that your second function will get executed right after your first function returns. You may see sometimes even 0ms which actually executes it right after first function returns.
If one would on the other hand use longer time i.e. 100ms this could result in a different function getting executed in the meantime and that could have an undesired effect on the whole UI process.
Why function queueing in the first place?
Browsers nowadays prevent client side functionality to hang current browser session by observing long running functions. If a particular function runs long enough, browser Javascript execution engine will pause it and ask the user whether they want to terminate it (kill it) or wait for it to complete.
This is usually undesired effect when you actually do have a long running function. For instance imagine you have a function that has to loop through a large number of items processing each one during the process. You definitely don't want the user to terminate the process because the loop needs to execute.
What's the solution in this case? In such case instead of having a single function with loop and executing it, you'd rather have the loop (queueing) function that would then queue function calls for processing each item. This is just an outer skeleton of such functionality.
function queueItems(items) {
for(var i = 0; i < items.length, i++)
{
setTimeout((function(item) {
return function() {
processItem(item);
};
})(items[i]), 0);
}
}
function processItem(item) {
// process individual item
}
This way you'd prevent your functions to run too long and after each executed function control would get back to Javascript engine resetting its function-hang timer. But be aware that while your functions are being executed your UI will likely be unresponsive or at most unpredictable. It may be better to queue your function with some time space in between so UI stays responsive if that's desired.
It's an old hack. If an event needs to be triggered after another event you can use setTimeout with 1ms to make sure the event is triggered after the other event.
I think that since trigger('update') internally has a setTimeout, only by setting another setTimeout you can achieve the desired order of statement execution. If you don't call 'sorton' through setTimeout it will be executed before 'update'.
On the other hand I guess 'update' uses setTimeout for preventing 'update' from being a blocking function when it may take a long time to be executed.
I have a Div that uses jQuery to load a file/contents with a javascript function..
function DoWork() {
// Do Stuff
}
Let's say the user can reload the Div and pull the same file/contents with the same js function DoWork(). The problem is, when the file is reloaded, the previous loaded function DoWork() is still running. How can I kill the previous fired DoWork() and restart it?
Javascript is single-threaded, which means only one thing can be executing at a given moment. If DoWork is already "running" it's either a) blocking all other JS code, and you have no choice but to let it finish since you have no way to execute any interruption code until it finishes on its own, or b) DoWork is scheduled to fire off on an interval via setTimeout() or setInterval().
If it's the latter case, setTimeout() and setInterval() return an ID. Store that ID somewhere and call clearTimeout(doWork_timeout_id) or clearInterval(doWork_interval_id) according to how you started it.
You can build a simple function that use: setTimeout and then each call to DoWork will call first to: clearTimeout. I don't really like this solution because you will waste CPU on setTimeout.
So another option will be to use web worker in DoWork (It will do lots of other good things for you in case you are working with big data as it's running in another thread) - then you get an option to send 'stop' message each time you start the work of DoWork().
Are you using ajax to load the div's contents? if so, the better way is as follows:
var doWorkAjax=null;
function DoWork(){
if (doWorkAjax) doWorkAjax.abort();
doWorkAjax = $.ajax(url, data, function(result){
....
doWorkAjax=null;
});
}
Sorry about the title but could not come up with anything really informative and concise.
My situation is that I am launching a setTimeout and I want it to be able to run in the middle of a JS function (it is used to get around the issue with injecting GM functions into the web page).
unsafeWindow.testG = function(key, dValue){
var rValue;
setTimeout(function(){rValue = GM_getValue(key, dValue);}, 0);
alert(rValue);
alert(rValue);
return(rValue);
}
In the three tests rValue is still undefined (which makes sense because JS is single threaded for the most part).
So I have 2 solutions I have thought of.
Favourite:
Is there anyway to tell JS to sleep in the middle of a function and work on background stuff (like Timeouts)?
Other:
Does anyone know when this timeout will be called? Maybe after this function execution but before whatever called it starts up again?
In that case making rValue global would solve the issue (but make slightly messier coding).
Or will it wait until all JS is done executing?
In that case I would possibly need another setTimeout to process the result.
There is no way what you're asking for can be accompished. Until HTML5 is a wide spread standard, you can't do what you're asking without thinking asynchronously.
For example :
unsafeWindow.testG = function(key, dValue, callback){
var rValue;
setTimeout(function(){
rValue = GM_getValue(key, dValue);
callback(rValue);
}, 0);
}
and call this with a callback :
unsafewindow.testG(key, dValue, function(rValue) {
alert(rValue);
});
alert("foo");
For the last sippet, "foo" will be echoed before rValue, because testG will execute the timeout function only when the Javascript thread is available, or only when there's no other script running.
To answer your first question, there is no 'sleep' function in JS. In fact, there is a site devoted to trying to create one: http://www.devcheater.com/ The conclusion: you cannot.
If what you'd like to do is make the rest of your code run later, you can put that code in a function and setTimeout().
Of course, the usual way to handle the sort of scenario you have set up is with callbacks. Since you're basically waiting for the thing in setTimeout to happen, you can have it call the rest of your code whenever it's done. For example:
var fartResult
function waitAMinuteThenFart (callback) {
function fart () {
fartResult = 'fart'
callback(fartResult)
}
setTimeout(fart, 1000*60)
}
waitAMinuteThenFart(function (result) { alert(result) })