I have a hefty script to run synchronously and I want to display a message like "Processing, please wait..." before it runs so the user isn't wondering why the page is frozen for a few seconds. I'm trying to do something like:
messageBox.html("Processing, please wait...");
// run hefty script
messageBox.html("Finished!");
But the page blocks before the message is displayed, even though the messageBox.html() statement comes first. Why is this?
Sometimes it makes sense to fire the "hefty script" in a timeout.
messageBox.html("Processing, please wait...");
setTimeout(function () {
heftyScript();
messageBox.html("Finished!");
}, 1);
The reason this happens is because it often holds UI updates until the end of the event loop (after your "hefty" script has finished). Setting a timeout ensures that hefty script doesn't run until a subsequent iteration of the event loop (letting the UI update at the end of the current iteration beforehand).
I would consider Web Workers in this case: http://www.html5rocks.com/en/tutorials/workers/basics/
UPDATE:
If for some reason you cannot use them then you should split your processing on smaller chunks and run them asynchronously. Locking UI is not an option.
Here is what you can do:
function heftyScript() {
var arr = [...];
var chunk_start = 0;
function do_chunk() {
for( var i = 0; i < 100; ++i ) { // 100 items per chunk
if( chunk_start >= arr.length)
return;
process( arr[chunk_start++] ); // process one element
}
window.setTimeout(do_chunk,10);
}
do_chunk();
}
It depends where the time is being spent. If it's downloading and parsing the JavaScript file, the messageBox.html() must be pure html or you do it in a script block before referencing the external file. If the time spent is running that long function then setTimeout(function () { heftyScript(); messageBox.html('finished'); }, 1); works wonderfully.
Related
In the next code, I want to process several files at the same time without wait to the end of each other. For this reason, I first read the files (array) and then the callback is called to process an element of this array instance.
I have found a problem into this javascript code, exactly in a async for-loop, where this process is executed as a sync code instead of async.
var array = ['string1','string2','string3','string4'];
function processArray (arrayString,callback){
//Read file Example.csv thought sync way
try{
var ifs = new InputFileStream('Example.csv','utf8');
table = ifs.read(0);
ifs.close();
}catch(err){
console.log(err.stack);
}
callback(arrayString, table);
}
//Async for
for (var i=0; i<array.length; i++) {
processArray(array[i], function(arrayString, table){
//Here process the file values thought async way
console.log('processed_'+i);
});
}
You could put the call back in a setTimeout with a delay of 1ms. That will run it in the next block of execution and your loop will continue on.
e.g. use this:
setTimeout(function() { callback(arrayString, table); }, 1);
instead of this:
callback(arrayString, table);
An alternative to this is to run the callback on a separate thread using Web Workers. I don't think it would appropiate to provide a long answer describing how to do multi threaded JavaScript here so I'll just leave the link to the docs. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
where this process is executed as a sync code instead of async
I've seen that you just have find out the answers of your question, remember that JavaScript is single thread.
So, for that when you execute operations that require full use of CPU like for..loops, while, etc; you just will get your code running synchronous and not only that,
You will get your web page freeze if they are huge loops
Let me give you an example, this is a while loop that will run for 6 seconds, look how you cannot do anything in stackoverflow.
function blocker (ms) {
console.log('You cannot do anything')
var now = new Date().getTime();
while(true) {
if (new Date().getTime() > now +ms)
return;
}
}
blocker(6000) //This stop your entire web page for 6 seconds
If you really want to achieve running blocking code in the background read about Web Workers or you just can use a small library I wrote, that allow you to execute a blocking CPU function in the background, I called it GenericWebWorker
I'm building a dynamic website that loads all pages inside a "body" div via jquery's load(). The problem is I have a script looped with setInterval inside the loaded PHP page, the reason being I want the script loaded only when that page is displayed. Now I discovered that the scripts keep running even after "leaving" the page (loading something else inside the div without refresh) and if I keep leaving / returning the loops stack up flooding my server with GET requests (from the javascript).
What's a good way to unload all JS once you leave the page? I could do a simple dummy var to not load scripts twice, but I would like to stop the loop after leaving the page because it's causing useless traffic and spouting console errors as elements it's supposed to fill are no longer there.
Sorry if this has already been asked, but it's pretty hard to come up with keywords for this.
1) why don't you try with clearInterval?
2) if you have a general (main) function a( ) { ... } doing something you can just override it with function a() { }; doing nothing
3) if you null the references to something it will be garbage collected
no code provided, so no more I can do to help you
This really sounds like you need to reevaluate your design. Either you need to drop ajax, or you need to not have collisions in you method names.
You can review this link: http://www.javascriptkit.com/javatutors/loadjavascriptcss2.shtml
Which gives information on how to remove the javascript from the DOM. However, modern browsers will leave the code in memory on the browser.
Since you are not dealing with real page loads/unloads I would build a system that simulates an unload event.
var myUnload = (function () {
var queue = [],
myUnload = function () {
queue.forEach(function (unloadFunc) {
undloadFunc();
});
queue = [];
};
myUnload.add = function (unloadFunc) {
queue.push(unloadFunc);
};
return myUnload;
}());
The code that loads the new pages should just run myUnload() before it loads the new page in.
function loadPage(url) {
myUnload();
$('#page').load(url);
}
Any code that is loaded by a page can call myUnload.add() to register a cleanup function that should be run when a new page is loaded.
// some .js file that is loaded by a page
(function () {
var doSomething = function () {
// do something here
},
timer = setInterval(doSomething, 1000);
// register our cleanup callback with unload event system
myUnload.add(function () {
// since all of this code is isolated in an IIFE,
// clearing the timer will remove the last reference to
// doSomething and it will automatically be GCed
// This callback, the timer var and the enclosing IIFE
// will be GCed too when myUnload sets queue back to an empty array.
clearInterval(timer);
});
}());
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
I have a generic Javascript code snippet which all the clients add to their website. This code snippet fetches a JS library, which has some important functions which should be called if the library is fetched in time. If the library is not fetched in time, then those functions should never be called.
To implement this, I have setup a timeout which has a callback function which takes care of it(which sets a variable depending on which those important functions will be either called or not). Now, it works perfectly in most of scenarios except when the client's website already has some timeouts/intervals with very small timer value.
Please see the fiddle http://jsfiddle.net/tmckM/37/, to see the issue.
I need to find a generic way to achieve this, so that if the library is fetched in time then the timeout doesn't occur in any case.
Following is the code used in JSFiddle
//Though the library file is downloaded in time(which can be seen from network tab) but still the timeout fires before the library execution. I need to find a workaround for this issue
var library_timeout = 1000;
//All time values are in milliseconds
function loadLibrary() {
var b = document.createElement('script');
b.src = 'http://yourjavascript.com/35211527623/library.js';
b.type = 'text/javascript';
document.getElementsByTagName('head')[0].appendChild(b);
}
function wasteTime() {
if (!wasteTime.counter) {
wasteTime.counter = 1;
}
else {
wasteTime.counter++;
}
if (wasteTime.counter == 5) {
clearInterval(wasteTimerId);
}
console.warn('Start wasting time');
var initial = Date.now();
while (true) {
if (Date.now() - initial > 1000) {
break;
}
}
console.warn('Stopped wasting time');
}
function startProcess() {
window.process_started_at = Date.now();
console.log('Started the process at timestamp:', process_started_at);
setTimeout(function () {
window.lib_timeout_fired_at = Date.now();
console.log('Library timed out at timestamp:', lib_timeout_fired_at);
console.log('So, though the library file will still download, but the functions in it won\'t be called.');
}, library_timeout);
loadLibrary();
}
//The following line is implemented on user's website.I can't change it.
wasteTimerId = setInterval(wasteTime, 0);//If this line is skipped then library is always executed first and then timeout occurs.
startProcess();
I don't see an issue here. The lib loading time can vary, the wasteTime js load can vary, and so can timeouts. The browser may be quite free to first execute the loaded script or fire the timeout if both are scheduled.
The solution to this is not using a timeout at all. Just change the
if(window.lib_timeout_fired_at)
in your library script to (you have all the variables avaiable already):
if (lib_started_at - process_started_at > library_timeout)
Of course you might rename/prefix them, so the overall solution might look like
window.lib_timeout_firing_at = Date.now() + 1000;
…
if (Date.now() > lib_timeout_firing_at)
I have to use atleast 2 setTimeouts and 1 setInterval. Does this have any dependency on the browser or javascript engine being used?
tl;dr: Don't worry about the cost of timers until you're creating 100K's of them.
I just did a quick test of timer performance by creating this test file (creates 100K timers over and over):
<script>
var n = 0; // Counter used to verify all timers fire
function makeTimers() {
var start = Date.now();
for (var i = 0; i < 100000; i++, n++) {
setTimeout(hello, 5000);
}
console.log('Timers made in', Date.now() - start, 'msecs');
}
function hello() {
if (--n == 0) {
console.log('All timers fired');
makeTimers(); // Do it again!
}
}
setTimeout(makeTimers, 10000); // Wait a bit before starting test
</script>
I opened this file in Google Chrome (v54) on my circa ~2014 Macbook Pro, and went to the Timeline tab in Developer Tools and recorded the memory profile as the page loaded and ran thru 3-4 cycles of the test.
Observations
The timer creation loop takes 200ms. The page heap size starts at 3.5MB pre-test, and levels out at 3.9MB.
Conclusion
Each timer takes ~.002 msecs to set up, and adds about 35 bytes to the JS heap.
On a page you can have as many setTimeouts/setIntervals running at once as you wish, however in order to control each individually you will need to assign them to a variable.
var interval_1 = setInterval("callFunc1();",2000);
var interval_2 = setInterval("callFunc2();",1000);
clearInterval(interval_1);
The same code above applies to setTimeout, simply replacing the wording.
As Kevin has stated, JavaScript is indeed single threaded, so while you can have multiple timers ticking at once, only one can fire at any one time - i.e. if you have one that fires a function which 'halts' in execution, for example with an alert box, then that JS must be 'resumed' before another can trigger I believe.
One further example is given below. While the markup is not valid, it shows how timeouts work.
<html>
<body>
<script type="text/javascript">
function addThing(){
var newEle = document.createElement("div");
newEle.innerHTML = "Timer1 Tick";
document.body.appendChild(newEle);
}
var t1= setInterval("addThing();",1000);
var t2 = setInterval("alert('moo');",2000);
</script>
</body>
</html>
You can use as many as you want. Just remember that JavaScript is single threaded, so none of them can execute in parallel.
var interval_1 = setInterval("callFunc1();",2000); calls eval() which is evil so it's BAD.
Use this instead var interval_1 = setInterval(callFunc1,2000);
And for the question, you may use as many as you want but if all have the same interval between two actions, you better do it this way
var interval = setInterval(function() {
// function1
fct1();
// function2
fct2();
},2000);