Angular Events, UI Blocked - javascript

I am making a general loading animation for my angular application.
I have a service which toggles a loading animation on and off and the events are happening as I expect them to, my issue is that the UI is not updating with the events.
The turn on and turn off of the loading animation occurs in the same function call so my guess is that the ui isn't updated until the function call completes which basically means that my turning on and off or the loading animation has no effect.
How can I write this in a way that the UI will be updated as the variables change. I added in a pause to simulate heavy calculation on load just to check... which is the datetime stuff.
The two broadcasts are the start and stop of the loading state.
function activateController(promises, controllerId) {
var startData = { controllerId: controllerId };
$broadcast(configcommonProvider.config.controllerActivateStartEvent, startData);
return $q.all(promises).then(function (eventArgs) {
var e = new Date().getTime() + (2 * 1000);
while (new Date().getTime() <= e) { }
var successData = { controllerId: controllerId };
$broadcast(configcommonProvider.config.controllerActivateSuccessEvent, successData);
});
}

Try calling $apply() on your scope object after the broadcast, it should force a digest cycle which should update the UI.

JavaScript is single-threaded and - conceptually - the changes in the GUI are on the same thread as the program.
This is exactly why there are so many callbacks in Javascript APIs (which you might have noticed working with AJAX). The only way to get around the single-threaded nature of JS is not to wait for something to finish at all. Instead - to be called back when something is finished.
What you want to do (long running calculations) is something very unnatural in JS, so the only real solution will be quite verbose. You need to run a webworker in parallel to your code and wait until it signals that it is finished. The shortest no-nosense example of using webworkers is probably the official HTML5 specification on WHATWG site.

Related

Why isn't this code executing synchronously?

I was under the impression that all DOM manipulations were synchronous.
However, this code is not running as I expect it to.
RecordManager.prototype._instantiateNewRecord = function(node) {
this.beginLoad();
var new_record = new Record(node.data.fields, this);
this.endLoad();
};
RecordManager.prototype.beginLoad = function() {
$(this.loader).removeClass('hidden');
};
RecordManager.prototype.endLoad = function() {
$(this.loader).addClass('hidden');
};
The Record constructor function is very large and it involves instantiating a whole bunch of Field objects, each of which instantiates some other objects of their own.
This results in a 1-2 second delay and I want to have a loading icon during this delay, so it doesn't just look like the page froze.
I expect the flow of events to be:
show loading icon
perform record instantiation operation
hide loading icon
Except the flow ends up being:
perform record instantiation operation
show loading icon
hide loading icon
So, you never even see the loading icon at all, I only know its loading briefly because the updates in the chrome development tools DOM viewer lag behind a little bit.
Should I be expecting this behavior from my code? If so, why?
Yes, this is to be expected. Although the DOM may have updated, until the browser has a chance to repaint, you won't see it. The repaint will get queued the same way as all other things get queued in the browser (ie it won't happen until the current block of JavaScript has finished executing), though pausing in a debugger will generally allow it to happen.
In your case, you can fix it using setTimeout with an immediate timeout:
RecordManager.prototype._instantiateNewRecord = function(node) {
this.beginLoad();
setTimeout(function() {
var new_record = new Record(node.data.fields, this);
this.endLoad();
}, 0);
};
This will allow the repaint to happen before executing the next part of your code.
JavaScript is always synchronous. It mimics multi-threaded behavior when it comes to ajax calls and timers, but when the callback gets returned, it will be blocking as usual.
That said, you most likely have a setTimeout in that constructor somewhere (or a method you're using does). Even if it's setTimeout(fnc, 0).

Javascript syncronous call with update

I have a function that performs a long task. I would like to create a function that is able to notify the caller of the progress. Ultimately I want to update the UI with the current progress.
Something like this:
function myLongMethod(progressCallback)
{
for(var i = 0 ... )
{
progressCallback(i) ;
}
}
This works but updates on UI are not smooth. Is there a better way? I would prefer something with a jquery Deferred object using deferred.notify(). Any ideas?
Your code is fine. You have got another problem. Javscript always runs on the UI thread. Your operation is blocking this thread (the browser) and you will see some blocking of your browser window.
Luckily there is a workaround implemented in modern browser called web workers. It's simple just call in your main script another script which then get executed:
var w = new Worker("another_script.js");
If your worker is ready you can react on the result by adding a event listner to the worker:
w.onmessage = function(event) {
//do something
}
When you use this pattern, your UI did not block. You can even return data from a web worker and include scripts into it. More details you can find here and here is a good starting tutorial.
Hi you can apply the easing effect to your UI for smoothness and i am giving the following code it may help you
var oldProgress = 0;
var uiUpdater = null;
function updateUI(newProgress){
if(uiUpdater !=null){
// update your ui to the old progress first
window.clearInterval(uiUpdater); // clearing the previous timer
}
var diff = newProgress - oldProgress;
oldProgress = newProgress;
var stepSize = diff/5; // applying the new change in 5 steps to the UI
uiUpdater = window.setInterVal(function(){
// updating your UI after every 100 milliseconds
// to give the smoothness
diff -= stepSize; // decreasing the difference gradually
if(diff<=0){
window.clearInterval(uiUpdater); // clearing the interval once the update is done
}
},100);
}
You have to call the "updateUI" method from you callback with the new progress.

javascript set interval run as separate thread?

I want to use a timer as a fallback in case I end up in an infinite loop. It seems that set interval is the right way to do this. However, it's not working for me.
From my research, it seems like setInterval should run in a separate thread in the background, but I don't see it.
Why is this behavior happening? And how do I solve this?
var time = 0;
window.setInterval(function(){time++;}, 1000);
while (true) {
//stuff done
if (time >= 5) {
break;
}
}
Browser javascript runs in a single thread. So if you perform something that takes too long - it will freeze browser.
See John Resig article for further details: http://ejohn.org/blog/how-javascript-timers-work/
After you read that article you'll get that your setInterval callback queued to be run in 1000ms after now but only after the current code is finished. It cannot finish though, because of the infinite loop.
zerkms has the correct answer. But I would add that web workers are a way to get some multi-threaded-ish behavior from client side javascript.
var worker = new Worker('my_task.js');
worker.onmessage = function(event) {
console.log("Called back by the worker!\n");
};
The worker runs in a background thread, and you can exchange messages and subscribe to events. It's pretty nifty.
As has been already said - the callback to setInterval doesn't run until the infinite loop finishes. To do what you are trying to achieve - without using web workers - you have to check the time from the loop itself:
var start = Date.now();
while((Date.now() - start) < 5000){
...
}

JavaScript parallel thread for DOM related processing

I have a resource consuming DOM (browser) related JavaScript process. When started it blocks the page (other DOM related processed). Is it possible to run this process in parallel asynchronously, and when it finished pass the result to main page? Web workers is not the case, as process works with DOM.
Can this be implemented with iframes? Does JS started in iframe block hosted page DOM too?
Actually it would be a prime example for using WebWorkers, but as you correctly mentioned you won't have a reference to a DOM there. Your only option is to decouple that process into smaller task's. If you can do that, you need to ask yourself two questions
do the smaller tasks need to run sequentially ?
can these tasks run asynchronously ?
If you can answer both questions with No, you can setup a run-away script timer and execute those tasks asynchronously. Example:
var taskList = [
function() {},
function() {},
function() {},
function() {}
// a whole lot more entrys
]; // in a real-world scenario you would `.push()` values or functions
(function _loop() {
setTimeout(function() {
var start = Date.now();
do {
taskList.shift()();
} while( taskList.length && Date.now() - start < 100)
if( taskList.length ) setTimeout( _loop, 100 );
},100);
}());
The above algorythm would execute the functions contained by taskList as fast as possible, but within a time-frame of 100ms max. This will ensure that the browser respectively the UI thread won't get blocked for longer than 100ms during the processing. Hence the browser will stay responsive.

Using setTimeout to improve responsiveness

When looking to improve a page's performance, one technique I haven't heard mentioned before is using setTimeout to prevent javascript from holding up the rendering of a page.
For example, imagine we have a particularly time-consuming piece of jQuery inline with the html:
$('input').click(function () {
// Do stuff
});
If this code is inline, we are holding up the perceived completion of the page while the piece of jquery is busy attaching a click handler to every input on the page.
Would it be wise to spawn a new thread instead:
setTimeout(function() {
$('input').click(function () {
// Do stuff
})
}, 100);
The only downside I can see is that there is now a greater chance the user clicks on an element before the click handler is attached. However, this risk may be acceptable and we have a degree of this risk anyway, even without setTimeout.
Am I right, or am I wrong?
The actual technique is to use setTimeout with a time of 0.
This works because JavaScript is single-threaded. A timeout doesn't cause the browser to spawn another thread, nor does it guarantee that the code will execute in the specified time. However, the code will be executed when both:
The specified time has elapsed.
Execution control is handed back to the browser.
Therefore calling setTimeout with a time of 0 can be considered as temporarily yielding to the browser.
This means if you have long running code, you can simulate multi-threading by regularly yielding with a setTimeout. Your code may look something like this:
var batches = [...]; // Some array
var currentBatch = 0;
// Start long-running code, whenever browser is ready
setTimeout(doBatch, 0);
function doBatch() {
if (currentBatch < batches.length) {
// Do stuff with batches[currentBatch]
currentBatch++;
setTimeout(doBatch, 0);
}
}
Note: While it's useful to know this technique in some scenarios, I highly doubt you will need it in the situation you describe (assigning event handlers on DOM ready). If performance is indeed an issue, I would suggest looking into ways of improving the real performance by tweaking the selector.
For example if you only have one form on the page which contains <input>s, then give the <form> an ID, and use $('#someId input').
setTimeout() can be used to improve the "perceived" load time -- but not the way you've shown it. Using setTimeout() does not cause your code to run in a separate thread. Instead setTimeout() simply yields the thread back to the browser for (approximately) the specified amount of time. When it's time for your function to run, the browser will yield the thread back to the javascript engine. In javascript there is never more than one thread (unless you're using something like "Web Workers").
So, if you want to use setTimeout() to improve performance during a computation-intensive task, you must break that task into smaller chunks, and execute them in-order, chaining them together using setTimeout(). Something like this works well:
function runTasks( tasks, idx ) {
idx = idx || 0;
tasks[idx++]();
if( idx < tasks.length ) {
setTimeout( function(){ runTasks(tasks, idx); },1);
}
}
runTasks([
function() {
/* do first part */
},
function() {
/* do next part */
},
function() {
/* do final part */
}
]);
Note:
The functions are executed in order. There can be as many as you need.
When the first function returns, the next one is called via setTimeout().
The timeout value I've used is 1. This is sufficient to cause a yield, and the browser will take the thread if it needs it, or allow the next task to proceed if there's time. You can experiment with other values if you feel the need, but usually 1 is what you want for these purposes.
You are correct, there is a greater chance of a "missed" click, but with a low timeout value, its pretty unlikely.

Categories

Resources