JavaScript callback - multithreading - javascript

I want to develope a very simple strategic game.
You can find an example on http://wiesenberg.info/hope/ !
If you click on a button, that project will be created (progressbar). After it is finished, the project will show up on the playground. As you can see it works if you click one by one. But once you click a button twice (start 2 project of the same kind), the second project will not be finished because the first has not finished and increased the variable. Is there an easy way of doing multithreading or just solving my bug?
I also want to add a function that you can stop the project by clicking on the progressbar. I tried it out, it dissapears in the list, but the icon still shows up, because the function didnt get that another function was executed. so I also need a break function for the progressbar. I hope you undertood my problem! Thanks
Heres the important code:
function addSygehus() {
sygehusBarID++;
sygehusID++;
$("#addProject").append("<div id=sygehus" + sygehusID +
" class='progress progress-striped active'><div class='progress-bar progress-bar-success' id=sygehusbar"
+ sygehusBarID + " aria-valuetransitiongoal='100'>Sygehus</div></div>");
function countdown(callback) {
var bar = document.getElementById("sygehusbar"+sygehusBarID),
time = 0, max = firsttime,
int = setInterval(function() {
bar.style.width = Math.floor(100 * time++ / max) + '%';
if (time - 1 == max) {
clearInterval(int);
// 600ms - width animation time
callback && setTimeout(callback, 600);
}
}, 1000);
}
countdown(function() {
$("#sygehus" + sygehusID).remove();
$("#sygehusPic" + sygehusID).show(1000);
});
}

I would look at JQuery Deferred (http://api.jquery.com/jQuery.Deferred/) and Promises Documentation in general to manage what your trying to achieve. So you can apply some control to the order in which the asynchronous calls occur.
You've also got Q.js (https://github.com/kriskowal/q) is a nice library for managing these kinds of scenarios.
Otherwise you have web workers as already mentioned for emulating multithreaded behaviour but I don't think that's what your looking for.

Related

variable timer with setTimeout() inside jQuery .each

I want to cycle through a bunch of jpg pictures as a slideshow. I have been using setInterval with success so far. However, I now want to have each slide show for a custom time. For example slide 1 shows for 6 seconds, then slide 2 for 3 seconds, etc. I tried the following code:
var sl = [["PodLoop1.jpg", 6000], ["PodLoop2.jpg", 3000]];
$.each(sl, function(i, value) {
fl = '<img src="media/' + value[0] + '" height="100%">'
setTimeout(function(){
$("#InnerMedia").html(fl);
if (i >= sl.length) {
window.location.href = "./media.php"; // refresh to restart from the beginning
}
}, value[1])
});
But this doesn't work. It just jumps to the last slide in the list. What am I doing wrong? I have looked at several similar questions on SO, for example this one, but none of them seem to deal with a variable timer.
i will never be equal sl.length
change the code to i >= sl.length-1
Your issue is you are using .each loop.
setTimeout
What setTimeout does is it registers an event at designated time. Now when you run this in loop, this will register 2 events, 1 at after 3secs and another at after 6secs. So it runs correctly.
To fix this, you will have to chain initialisation of these setTimeouts. You will have to register new event inside another one.
Sample
function createTimeout(arr, index){
if(!arr[index] == undefined) return;
setTimeout(function(){
console.log(arr[index][0]);
createTimeout(arr, ++index)
}, arr[index][1])
}
var sl = [["PodLoop1.jpg", 6000], ["PodLoop2.jpg", 3000]];
createTimeout(sl, 0);

consecutives calls to requestAnimationFrame

I played a bit with the javascript function called requestAnimationFrame(), and made this stroke by stroke kanji painter. It works well... the code is clean enough, considered my newbie-ness. I followed a nice tutorial here and landed on a structure like this :
function here_a_closure() {
var some_state = 0;
var last_frame = false;
function machinery() {
// do mysterious stuff
return last_frame;
}
function frame_display() {
handle = window.requestAnimationFrame(frame_display);
if (machinery()) {
window.cancelAnimationFrame(handle);
}
// do the display
}
frame_display();
}
However, I would like to extend this, and paint some more kanjis next to the first one. Should I wait for the end of the first animation to launch the next one ? I would prefer (more modularity and re-use), but how ? Or should I make the machinery more complex to animate more than one character in the same animation ?
Use a single requestAnimationFrame to drive all your animations.
If you have multiple animations that run with different timings then you can make use of the elapsed time that's automatically fed into each call.
function animate(time){
requestAnimationFrame(animate);
}
This way you have the flexibility of running each of your animations either consecutively or serially without increasing the complexity of your 'machinery'.
if(time>doSomethingOnAnimation1whenThisTimeElapses){ ... }
if(time>doSomethingOnAnimation2whenThisTimeElapses){ ... }
if(time>doSomethingOnAnimation3whenThisTimeElapses){ ... }
Here's a more detailed code about using requestAnimationFrame's timer on a previous post:
How to control animation speed (requestAnimationFrame)?

Update DOM while iterating over Backbone Collection?

So I am trying to display a loading bar when rendering very large collections. I have a placeholder for the loading bar when the page initially loads, and I am trying to update it like this:
addAll:
#collection.each(((obj, index) ->
#addOne(obj, index)), this
)
addOne: (obj, index) ->
percent_complete = ((index / #collection.length) * 100)
$(".loading_bar").width("#{percent_complete}%")
# Proceed with rendering, create the view, etc
The problem here is that the DOM isnt updated until the addAll function completes. I have a feeling that this is me not understanding some basic JS fundamentals. Any help would be greatly appreciated!
Yes, you are missing something fundamental: the browser won't do any of its own work until your code returns control to the browser.
Consider some code like this:
collection = [1..1000]
addOne = (index) ->
$('#n').text(index + 1)
percent_complete = ((index + 1) / collection.length) * 100
$("#bar").width("#{percent_complete}%")
addOne(i) for e,i in collection
console.log('done')
You'll see a brief pause and then #bar and #n will be updated and done will appear in the console. Demo: http://jsfiddle.net/ambiguous/f5qKV/ (you might need to increase the 1000 to make things more obvious).
However, if you return control to the browser with setTimeout(..., 0) on each iteration:
collection = [1..1000]
addOne = (index) ->
$('#n').text(index + 1)
percent_complete = ((index + 1) / collection.length) * 100
$("#bar").width("#{percent_complete}%")
i = 0
timeOut = ->
if(i == collection.length)
console.log('done')
return
addOne(i++)
setTimeout(timeOut, 0)
setTimeout(timeOut, 0)
you'll be able to see #bar and #n changing and then you'll see done in the console when everything has finished. Demo: http://jsfiddle.net/ambiguous/UCbY8/1/
Note that the setTimeout version uses the setTimeout callback to trigger the next timeout, that ensures that everything happens in the same order as they would in a simple for or each loop.
The lesson is that you have to add some old-school pseudo cooperative multi-tasking hacks into the mix if you want to use that kind of progress indicator.
Handing control back to the browser also leaves you open to user interaction when you're not expecting it. You might want to add a general UI blocker to keep people from clicking on things while you're working if you go this route.

In JavaScript, How to make make changes to document elements while complex functions are performed?

I'm processing a large amount of data with JavaScript. I put a circular gif and a progress div to show how much progress has been done in creating the model. However, when it gets to the bulky part of code processing, the loading gif stops spinning and the percentage updating stops working. (until the very end for a split second)
This is the block of code that freezes the gif.
// convert binary normals to ascii
for(i=0;i<norms.length;i++){ //<-- the array length is about 200,000, and could be larger
normals.push(toAscii(norms[i], mins[5], maxes[5])); //nx
normals.push(toAscii(norms[i+1], mins[6], maxes[6]));//ny
normals.push(toAscii(norms[i+2], mins[7], maxes[7]));//nz
i = i+2; //skip next 2 as they're already converted
percentComplete = (normals.length/norms.length)*100;
percentComplete = Math.round(percentComplete);
document.getElementById('loadingScrn').innerHTML = "Processing "
+percentComplete + "%" + " Complete"; //<-- The loading gif is right below this element on the webpage and neither update while this function is running
}
How can I get the browser update the display while JavaScript functions process large data? Is there a way to thread activities so that both updating the Document and JavaScript processing occur simultaneously?
JavaScript runs on the same thread as the browser GUI in most cases (or the tab's GUI, if each tab is given its own process). You will have to break the work into small pieces and schedule the next piece from the currently-executing one using setTimeout().
For example, this might work in your case:
var i = 0;
function doWork() {
do {
// One iteration here...
i++;
} while (i % 100 != 0 && i < norms.length);
// ^^^
// Break work into pieces of 100 elements each; adjust this
// number as needed.
if (i < norms.length) {
setTimeout(doWork, 1);
}
}
setTimeout(doWork, 1);
See this example jsfiddle.
You are correctly observing that JavaScript code runs in the same thread as the document's interface, blocking it when you perform large operations.
Web Workers are a JavaScript feature that is designed to help solve this problem. It allows you to spawn new threads that run along side the document, and communicate results asynchronously as they become available. Unfortunately this is not yet supported in Internet Explorer, but it is planned for IE10, and other browsers already support it.
As suggested by cdhowie and Jonathan M, another solution (inferior, but supported everywhere) is to use setTimeout to pause your code occasionally and let the browser respond to events. You would need to make your code somewhat more complicated to make this work. To give you an idea, to pause every 1000 items you would do something like this:
var workSliceSize = 1000;
var doWorkFromIndex = function(start) {
for (var i = start; i < norms.length; i++) {
if (i - start > workSliceSize) {
setTimeout(0, doWorkFromIndex, i + 1);
break;
}
normals.push... // your code here
}
}
doWorkFromIndex(0);
Try setTimeout(). It processes code asynchronously. I've used it to free up the screen processing by doing:
setTimeout(function() {
// the stuff I want to accomplish while keeping the gif going
},
0
);
Here I've set the timeout time period at zero milliseconds, but it can be whatever you want.
Try
var i = 0;
function processNormals() {
normals.push(toAscii(norms[i], mins[5], maxes[5])); //nx
normals.push(toAscii(norms[i+1], mins[6], maxes[6]));//ny
normals.push(toAscii(norms[i+2], mins[7], maxes[7]));//nz
i = i+3; //skip next 2 as they're already converted
percentComplete = (normals.length/norms.length)*100;
percentComplete = Math.round(percentComplete);
document.getElementById('loadingScrn').innerHTML = "Processing " +percentComplete + "%" + " Complete";
if(i < norms.length) setTimeout(processNormals, 20);
}

jQuery onClick execution

I have this bit of javascript written with jQuery 1.2.5. It's contained inside the main function() of a plugin that I wrote. The plugin is a horizontal gallery scroller very similar to jCarousel. It does alot of auto calculating of widths and determines how many to scroll based on that and the size of the images, which is what all the calculations are that are going on.
What my question is, how do I prevent this from firing off before a previous execution is finished. For instance, if I get a little click happy and just frantically mash down on .digi_next. Things don't go so well in the UI when that happens and I'd like to fix it :) I thought the answer might lie in queue, but all my attempts at using it haven't turned out anything worthwhile.
var self = this;
$(".digi_next", this.container).click(function(){
var curLeft = $(".digi_container", self.container).css("left").split("px")[0];
var newLeft = (curLeft*1) - (self.containerPad + self.containerWidth) * self.show_photos;
if (newLeft < ((self.digi_gal_width - (self.containerPad + self.containerWidth) * self.show_photos)) * -1) {
newLeft = ((self.digi_gal_width - (self.containerPad + self.containerWidth) * self.show_photos)) * -1;
}
$(".digi_container", self.container).animate({
left: newLeft + "px"
}, self.rotateSpeed);
});
Just use a global busy flag. When you enter your click handler, check it, and only proceed if it's false. Immediately set it to true, and then set it back to false when the animation ends. JavaScript is single-threaded, so there is no race condition to worry about.
var busy = false;
$("...").onclick(function() {
if (busy) return false;
busy = true;
$("...").animate(..., ..., ..., function() {
busy= false;
});
return false;
});
Take a look at jQuery UI. Specifically the effects-part of the plug in. I use the slide-effect on my personal website (click on the arrows at the sides of the boxes).
I prevent users triggering the effect more than once - before the effect has ended - with the one event-handler and a callback function.
Here's the source-code
As an alternative to the afformentioned global flag, you could assign the value to the DOM element, that way enabling multiple elements on the page to have the same behaviour:
$("...").onclick(function(el) {
var self = el;
if (self.busy) return false;
self.busy = true;
$("...").animate(..., ..., ..., function() {
self.busy= false;
});
return false;
});
Since JavaScript functions calls are asyncronus, you can pass as a in parameter a callback function that's called when the previous call ends (same for errors).
You can pass the function you wrote in this post as the callback for the function that fire before.
Hope this helps.
Regards

Categories