Number-count-up animation in a non-blocking way - javascript

I am trying to count a number up to a certain target value in an animation-like style. The environment is Titanium on iOS. I do that as follows.
function countNumberUp(label) {
label.currentVal = label.currentVal ? label.currentVal : 0;
setTimeout(function() {
if (label.currentVal < label.targetVal) {
label.currentVal += 1;
label.setText(label.currentVal);
countNumberUp(label);
}
}, 5);
}
label is an instance of Ti.UI.Label.
First problem I see is that the label.setText()-method is veeery slow. Would be cool if the number counts up in a rush but it's only like 5 steps per second even if I vary the second parameter of setTimeout().
The other thing is that the animation totally blocks die Main/UI thread of iOS and the UI hardly accepts any actions until the animation has finished. Seems like setTimeout does not run in a seperate thread.
Does anyone of you know a better way to do this?
Btw. I have also tried setInterval() but it doesn't seem any better.

Try this example:
function countNumberUp(label){
var interval = setInterval(function(){
if(label.currentVal==label.targetVal){
clearInterval(interval);
}else{
label.text=label.currentVal;
label.currentVal++;
}
//remove self-calling contNumberUp(label);
}, 50);
//5ms delay its cause bad performance
}

Related

jquery setTimeout too much recursion

I have read from multiple places that setTimeout() is preferable to setInterval() when setting something up to basically run forever. The code below works fine but after about an hour of running Firefox (38.0.1) throws an error of too much recursion.
Essentially I have it grabbing a very small amount of text from counts.php and updating a table with that information. The whole call and return takes about 50ms according to the inspectors. I'm trying to have it do this every x seconds as directed by t.
I suspect if I switch to setInterval() this would probably work, but I wasn't sure what the current state of the setTimeout() vs setInterval() mindset is as everything I've been finding is about 3-5 years old.
$(document).ready(function() {
t = 3000;
$.ajaxSetup({cache: false});
function countsTimer(t) {
setTimeout(function () {
$.getJSON("counts.php", function (r) {
$(".count").each(function(i,v) {
if ($(this).html() != r[i]) {
$(this).fadeOut(function () {
$(this)
.css("color", ($(this).html() < r[i]) ? "green" : "red")
.html(r[i])
.fadeIn()
.animate({color: '#585858'}, 10000);
})
};
});
t = $(".selected").html().slice(0,-1) * ($(".selected").html().slice(-1) == "s" ? 1000 : 60000);
countsTimer(t);
});
}, t);
};
countsTimer(t);
});
Update: This issue was resolved by adding the .stop(true, true) before the .fadeOut() animation. This issue only occurred in Firefox as testing in other browsers didn't cause any issues. I have marked the answer as correct in spite of it not being the solution in this particular case but rather it offers a good explanation in a more general sense.
You should indeed switch to setInterval() in this case. The problem with setInterval() is that you either have to keep a reference if you ever want to clear the timeout and in case the operation (possibly) takes longer to perform than the timeout itself the operation could be running twice.
For example if you have a function running every 1s using setInterval, however the function itself takes 2s to complete due to a slow XHR request, that function will be running twice at the same time at some point. This is often undesirable. By using setTimout and calling that at the end of the original function the function never overlaps and the timeout you set is always the time between two function calls.
However, in your case you have a long-running application it seems, because your function runs every 3 seconds, the function call stack will increase by one every three seconds. This cannot be avoided unless you break this recursion loop. For example, you could only do the request when receiving a browser event like click on the document and checking for the time.
(function()
{
var lastCheck = Date.now(), alreadyRunning = false;
document.addEventListener
(
"click",
function()
{
if(!alreadyRunning && Date.now() - lastCheck > 3000)
{
alreadyRunning = true;
/* Do your request here! */
//Code below should run after your request has finished
lastCheck = Date.now();
alreadyRunning = false;
}
}
)
}());
This doesn't have the drawback setInterval does, because you always check if the code is already running, however the check only runs when receiving a browser event. (Which is normally not a problem.) And this method causes a lot more boilerplate.
So if you're sure the XHR request won't take longer than 3s to complete, just use setInterval().
Edit: Answer above is wrong in some aspects
As pointed out in the comments, setTimeout() does indeed not increase the call stack size, since it returns before the function in the timeout is called. Also the function in the question does not contain any specific recursion. I'll keep this answer because part of the question are about setTimeout() vs setInterval(). However, the problem causing the recursion error will probably be in some other piece of code since there is not function calling itself, directly or indirectly, anywhere in the sample code.

Timing in javascript sequencer

I have developed a music sequencer in javascript; something like this: http://stepseq.michd.me/
I have implemented loop using setInterval function in following way:
var Sequencer = {
...
// every step sequencer ...
next: function(callback) {
// restart from begin if arrive to last sequecer step
if(Sequencer.current==Sequencer.steps.length)
Sequencer.current = 0;
// play all sounds in array step contains
if(Sequencer.steps[Sequencer.current].length>0) {
var set = Sequencer.steps[Sequencer.current];
for(var i=0;i<set.length;i++){
set[i].play();
}
}
callback(Sequencer.current);
Sequencer.current++;
},
loop: function(callback) {
Sequencer.interval = $interval(function(){
Sequencer.next(callback);
}, Sequencer.time);
}
}
...
Code below works but i think that there is a better way to implement loop; infact sometimes steps goes out of time. Sequencer.time (time passed to setInterval function) is a time in millisecs (this value is the conversion of a bpm value; for example it can be 125),
Someone can suggest me a better solution?
N.B.: this is a web application angularjs based (for this reason in code above a use $interval insteed of setInterval), but i think that this point is insignificant.
Javascript timer intervals are not guaranteed to run at exactly the time you request, due to the single threaded nature of JS. What you get is a callback that is queued up to run after the interval expires, whenever the engine is free to run it.
John resig has covered this off in some depth:
http://ejohn.org/blog/how-javascript-timers-work/
http://ejohn.org/blog/analyzing-timer-performance/
And from his conclusions, which is going to be important for your app:
If a timer is blocked from immediately executing it will be delayed
until the next possible point of execution (which will be longer than
the desired delay).
I don't really have a better solution for your problem, due to these fundamental issues with timers in JS, but this may at least explain what is happening.

How to suspend JavaScript to allow render

I have a little script that runs a simulation, of which I want to render the results 'live':
for ( i < simulation steps ) {
do_simulation();
render_with_flot();
}
I noticed that the plot only gets rendered after the last step.
Is there a way to 'suspend' javascript somehow to allow the rendering to run after each iteration?
Or is there a way to make flot run synchronously?
Or do I need to set my own timeouts for each iteration of the for-loop? This seems like kind of a hassle.
Depends how fast it needs to run, but the best way would be to use SetInterval
Pseudocode / hand-written-javascript-that-probably-doesnt-run:
var PerformedSteps;
var Interval;
PerformedSteps = 0;
Interval = setInterval(work, 1000/60); //60 times/second
function work()
{
PerformedSteps++;
if (PerformedSteps == simulation_steps)
{
clearInterval(Interval);
return;
}
do_simulation();
render_with_flot();
}
As an alternative to #PhonicUK's solution, you could do a setTimeout() at the end of work() to schedule the next call to work(), giving the rendering a chance to happen & not tying yourself to any particular refresh rate.

how alert make javascript interrupt? can I do it with code?

I'm procesing a kind of "big" JSON object around of 4000 elements passing for different methods, and I would like to update a div tag with a text showing the process.
But for some strange reason (just tested in Firefox and Chrome), they don't update the DOM object with the text.
$("#estatusBar").text(_item.Centro_de_trabajo);
Both prefer to continue calculating all the data and other process without and dont take the time for update the text. But if I just code an Alert("") in the loop and then in chrome I click on the "selected box" saying ignore all other alerts, chrome suddenly starts updating the text.
So I was thinking if I can "pause" the calculation with some kind of code to stop and update the DOM element and then continue making the other process?
Is this possible or what is an alternative to this strange behavior?
-- EDIT --
this is the code of the loop
$.each(plantillas, function(_index, _item){
updateBar(_item.Centro_de_trabajo);
calculateItem(_item,_index);
a.push("<div class='blockee'><ul>"+ /*temp.join("")*/ t(_item) +"</ul></div>");
});
No you cannot do what alert does. This limitation is really annoying in some cases but if your problem is just a progress for a single long computation then the solution is simple.
Instead of doing alll the records in one single loop break the computation in "small enough" chunks and then do something like
function doit()
{
processBlockOfRecords();
updateProgressBar();
if (!finished()) {
setTimeout(doit, 0);
}
}
setTimeout(doit, 0);
With this approach is also simple to add an "abort" button to stop the computation.
In your example the loop is
$.each(plantillas, function(_index, _item){
updateBar(_item.Centro_de_trabajo);
calculateItem(_item,_index);
a.push("<div class='blockee'><ul>"+ /*temp.join("")*/ t(_item) +"</ul></div>");
});
so the computation could be split with (untested)
function processRecords(plantillas, completion_callback) {
var processed = 0;
var result = [];
function doit() {
// Process up to 20 records at a time
for (var i=0; i<20 && processed<plantillas.length; i++) {
calculateItem(plantillas[processed], processed);
result.push("<div class='blockee'><ul>" +
t(plantillas[processed]) +
"</ul></div>");
processed++;
}
// Progress bar update
updateProgress(processed, plantillas.length);
if (processed < plantillas.length) {
// Not finished, schedule another block
setTimeout(doit, 0);
} else {
// Processing complete... inform caller
if (completion_callback) completion_callback(result);
}
}
// Schedule computation start
setTimeout(doit, 0);
}
You can try using web workers, to defer the calculation to the background, and this should help you out. Web workers were designed to do this very thing (push large calculations to a background thread). However, this may not work in all browsers. Your main concern is IE, and only IE 10 supports it:
Do you want some version of wait or pause or sleep or similar in javascript is that it?
The only way is with
window.setTimeout()
You can pause any function with it.
Check this post too might help:
Sleep/Pause/Wait in Javascript

Loop kinetic javascript transitions one after other instead of all at once

I want to to queue several transitions one after the other in html5 canvas.
Looping the transition function calls all the transitions at once. I dont know if callback will be do this if the iterations are more than 100.
I want to do something like this:--
for(i=0;i<10;i++)
{
move(circle,Math.floor(Math.random()*1000),400);
}
move is my defined function which makes some transitions.its working perfectly fine.
Here, i want the circle to change its postion with every iteration but its changing its position only once.
You could do this:
var i=10;
var interval = window.setInterval(function(){
move(circle,Math.floor(Math.random()*1000), 400);
console.log(i);
if(!--i) {
window.clearInterval(interval);
}
}, 400); // wait 400 msecs between calls
Or, if your move function was willing to invoke a callback function once the transition was complete :
var i=10;
var callback = function(){
if(i--){
move(circle,Math.floor(Math.random()*1000),400, callback);
}
}
callback();
Yeah ofcource. Its not exactly the solution to the problem but sort of a trick.i first stored the instructions in a separate array (transitionsequence) and used a recursive callback to Callback (the callback defined in kinetic). its not very efficient method but i dont care as long as it solves the problem. :)
`function move2( i , limit) {
var obj = transitionsequence[i].object;
obj.transitionTo({
y:100,
duration: 0.3,
callback : function()
{
obj.transitionTo({
x:transitionsequence[i].x,
duration:0.3,
callback: function()
{
obj.transitionTo({
y:transitionsequence[i].y,
duration:0.3,
callback: function()
{
if(i < limit)
move2(i+1 , limit);
}
});
}
});
}
});
};`
The reason why your approach doesn't work is because the browser doesn't get an opportunity to repaint the canvas between your painting steps. Traditionally this was solved by rendering a single step (frame) and then waiting a small amount of time, but there's a new feature available in recent browsers: requestAnimationFrame, which is solving that exact problem.
Check Animating with javascript: from setInterval to requestAnimationFrame and requestAnimationFrame for Smart Animating (they also show how to create a shim for animating in browsers that don't support requestAnimationFrame).
(I don't know kinetic.js, but there might even be direct support for such a shim in it).

Categories