In a Angular.js and Socket.io App, I want to show a loading before sending an image via Socket.io. I write this code:
When a button clicked this code runs, first i want to run startLoading function and after that I want to send image:
$scope.startLoading(function(){
$scope.socket.emit('sendImg', {
data: $scope.newImg,
});
});
and this is my startLoading function:
$scope.startLoading = function (callback) {
//DO SOME STUFF LIKE ENABLING LOADING ANIMATION
$scope.animation =true; //binded to UI
$scope.errors = false; //binded to UI
callback(); //it seems this code runs before above lines
};
But it seems callback() line runs before first two lines and because of that, my loading appears after sending of image to the server! why? i change callback line to a timeout like this and it works fine but is this a good solution? i dont think! what i have to do for a standard code?
$scope.startLoading = function (callback) {
//DO SOME STUFF LIKE ENABLING LOADING ANIMATION
$scope.animation =true; //binded to UI
$scope.errors = false; //binded to UI
$timeout(function(){callback();}, 1000);
};
Actually, code runs sequentially but calling callback and sending image freezes page and because of that, my loading appears after freezing ends. but i need before freezing, loading starts
Your code really does run sequentially, but the first two lines don't change the UI immediately.
When you assign some value to a scope variable, it's just that, a variable assignment. It doesn't trigger any events. Angular will only update the UI later, when it evaluates the bindings and finds the change. So here is what happens:
$scope.startLoading = function (callback) {
// Presumably this is called from some event from Angular,
// so all this is run in an $apply, in an Angular "context".
// But this is still "plain" javascript, so the next two lines
// are just plain variable assignments.
$scope.animation =true;
$scope.errors = false;
callback(); // This function does its thing, then returns
// When this function returns, Angular will evaluate all of its
// bindings, will find that the above values have changed,
// and will update the DOM.
};
For details, see the "Integration with the browser event loop" section in the dev guide.
What you want is to ensure that the DOM is updated before your callback runs. I think there is nothing wrong with using $timeout for this.
There might be a better/nicer way, but I haven't found it yet...
So it would become something like this:
$scope.startLoading = function (callback) {
$scope.animation =true; // at this point, these are just
$scope.errors = false; // plain variable assignments
$timeout(callback); // schedule callback to run later
// After this returns, Angular will evaluate its bindings,
// and update the DOM, so if $scope.animation and $scope.errors
// are bound to something, they can trigger some visible change.
// callback will be called in the next $digest cycle, so _after_
// the DOM has been updated
};
(There is no need to specify a value for the timeout if you only want to run it in the next "tick". Also, there is no need to wrap callback, it can be directly passed to $timeout.)
Hope this helps!
Related
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 am trying to populate different cells in a table, named as #RECALRow1, #RECALCol1, #RECALBodySum. Each is populated from a database. I am using AJAX and, specifically, jQuery's load.
Originally I used a number of functions - see version 1 below - which worked (the code in these functions in effectively in version 2). This worked.
I belatedly realised how similar the code in these functions was. Version 2 shows the code without functions, illustrating the similarity. This worked too. (valTable is defined earlier - the definition is not shown below).
It then seemed "obvious" that I should write a generic function which just takes two parameters. Making three calls to this function, with different parameters, should surely work(!) In fact only the third function seems to have been called; the first two do not even succeed in generating a console message.
I wondered if I am missing something on callbacks - and I read How do I return the response from an asynchronous call? - but I cannot see that I need them. Perhaps I am about to learn something very basic on practical AJAX.
Version 1
Individual functions, each of which uses jQuery load. THIS WORKS.
[Aside - ASP sets the default selected value]
UpdateCol1Possibilities(); // sets content for #RECALCol1
UpdateRow1Possibilities();
UpdateBodySumPossibilities();
Version 2
Direct call to jQuery load, without encapsulation. THIS WORKS.
[Aside - we need to tweak the default selected value]
$('#RECALRow1').load(
"/_RECAL/AJAX/AJAXSetGroupbyList.asp", // URL
{ // data - no need for callback
"RECALtable":valTable,
"RECALCol1Row1Id":'RECALRow1'
}); // close load
$('#RECALCol1').load(
"/_RECAL/AJAX/AJAXSetGroupbyList.asp", // URL
{ // data - no need for callback
"RECALtable":valTable,
"RECALCol1Row1Id":'RECALCol1'
}); // close load
$('#RECALBodySum').load(
"/_RECAL/AJAX/AJAXSetGroupbyList.asp", // URL
{ // data - no need for callback
"RECALtable":valTable,
"RECALCol1Row1Id":'RECALBodySum'
}); // close load
Version 3
Generic function, which uses jQuery load. THIS DOESN'T WORK.
var RealSelect = _.debounce(function(IdToChange) {
console.log('Calling RealSelect changing Id: ' + IdToChange);
$('#' + IdToChange).load(
"/_RECAL/AJAX/AJAXSetGroupbyList.asp", // URL
{ // data
"RECALtable":$('#RECALtable').children().val(),
"RECALCol1Row1Id":IdToChange
}
, // callback
function() { // callback function - success
// alert('successful callback!');
} // close callback function, close load
) // close load
}
,50); // end RealSelect function
RealSelect('RECALCol1'); // sets content for #RECALCol1
RealSelect('RECALRow1');
RealSelect('RECALBodySum');
// Only #RECALBodySum is populated
}
I'm not sure why you felt you should use _.debounce, but that is precisely your problem, as far as I can see.
Remove the debounce wrapper in your function declaration and all should work:
var RealSelect = function(IdToChange) {
console.log('Calling RealSelect changing Id: ' + IdToChange);
$('#' + IdToChange).load(
"/_RECAL/AJAX/AJAXSetGroupbyList.asp", // URL
{ // data
"RECALtable":$('#RECALtable').children().val(),
"RECALCol1Row1Id":IdToChange
}
, // callback
function() { // callback function - success
// alert('successful callback!');
} // close callback function, close load
) // close load
}; // end RealSelect function
Debounce means precisely that your function will NOT be called until a certain amount of time has passed where no new calls were made.
This is the expected behaviour, for example, of an autocomplete widget. You don't want to process every keystroke the user does immediately. Instead, you wait for the user to stop typing for, say, 500 millis, and then start fetching the data from the server.
In your case, you are creating a debounced function and then calling it three times in a row.
Debounce is working as expected - only the last call runs, and only after 50 millis.
From the underscorejs docs:
debounce_.debounce(function, wait, [immediate])
Creates and returns a
new debounced version of the passed function which 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.
Pass true for the immediate parameter to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
I want to set delay in javascript code so that XML file generated before running of javascript . Here is my html code
<body onLoad="Func1Delay()">
<div id="map"></div>
</body>
In this Func1Delay() function i have written code to delay execution of javascript
function Func1Delay()
{
setTimeout("load()", 3000);
}
load() is javascript function ? how can i delay execution of javascript code so that xml file successfully generated before code execution??
Seems like your downloadUrl function provides a callback. The callback function fires automatically, after the XML is loaded. You do not need a 3 second delay, just move your logic inside the callback function. Something like this:
function Func1Delay() {
downloadUrl("location.xml", function (data) {
var xml = data.responseXML;
// do any thing with xml, it is loaded!
// alert(xml);
});
}
That's how you do it, except you don't want to use a string (although it works — provided you have a function called load defined at global scope). setTimeout schedules a function to be called a given number of milliseconds later.
It's better to give it an actual function reference:
function Func1Delay() {
setTimeout(load, 3000);
function load() {
// Stuff to do three seconds later
}
}
Note that the event you're using to trigger it, the onload of body, already happens really, really late in the page load cycle, and so whatever you're waiting for may already be done; conversely, if it might take more than three seconds, you might not be waiting long enough. So if there's something you can check to see whether it's done or not, you can poll, like this:
function Func1Delay() {
check();
function check() {
if (theWorkIsDone) {
// Do something with the work
}
else {
// Check back in 100ms (1/10th of a second)
setTimeout(check, 100);
}
}
}
You want the function to execute as soon as possible, but in every case after your xml has been successfully generated.
In this case you should prevent using a fixed amount of time (because you don't know the value exactly), but try the following:
function load(){
if (/*check here if the xml has *not yet* been generated*/){
setTimeout(load,50); // try again in 50 milliseconds
return;
}
// do your stuff here
}
This loops as long as your xml is not ready, and kicks in as soon as it's available.
General about setTimeout:
You can pass a string, but this is highly discouraged from for several reasons.
Instead pass a function reference or a function like this:
// function reference
setTimeout(load,3000) // no `()` !
// function
setTimeout( function(){load()},3000)
If you need paramters be passed to the function, you can't use the first option but need to use the second one, where you can easily pass them load(params).
If you pass a function like this: setTimeout(load(),3000) it executes the function load and passes its return value to the timeout. You however want the function invoked after 3 seconds and thus only pass the reference to the function.
Notice however, that you have a different scope if you execute the functions this way.
To speed up my application I want to prepare some data before DOM is ready and then use this data when DOM is ready.
Here's how it might be:
var data = function prepareData(){
...
}();
$(document).ready(function() {
// use data to build page
}
How to prepare the data for later use?
Thanks
You need should use parentheses around the function expression for clarity (and because in a similar situation where you're defining and calling a function but not using the return value, it would be a syntax error without them). Also, when you use a function expression, you want to not give it a name. So:
var data = (function(){
...
})();
or use a function declaration instead:
var data = processData();
function processData() {
...
}
(Why not use a name with a function expression? Because of bugs in various implementations, especially Internet Explorer prior to IE9, which will create two completely unrelated functions.)
However, it's not clear to me what you're trying to achieve. When the browser reaches the script element, it hands off to the JavaScript interpreter and waits for it to finish before continuing building the DOM (because your script might use document.write to add to the HTML token stream). You can use the async or defer attributes to promise the browser you're not going to use document.write, on browsers that support them, but...
Update: Below you've said:
because prepareData is long time function and I assumed that browser can execute this while it's building DOM tree. Unfortunately '$(document).ready' fires before prepareData is finished. The question is how to teach '$(document).ready' to wait for ready data
The only way the ready handler can possibly trigger while processData is running is if processData is using asynchronous ajax (or a couple of edge conditions around alert, confirm, and the like, but I assume you're not doing that). And if it were, you couldn't be returning the result as a return value from the function (though you could return an object that you continued to update as the result of ajax callbacks). Otherwise, it's impossible: JavaScript on browsers is single-threaded, the ready handler will queue waiting for the interpreter to finish its previous task (processData).
If processData isn't doing anything asynchronous, I suspect whatever the symptom is that you're seeing making you think the ready handler is firing during processData has a different cause.
But in the case of asynchronous stuff, three options:
If you're not in control of the ready handlers you want to hold up, you might look at jQuery's holdReady feature. Call $.holdReady(true); to hold up the event, and use $.holdReady(false); to stop holding it up.
It's simple enough to reschedule the ready handler. Here's how I'd do it (note that I've wrapped everything in a scoping function so these things aren't globals):
(function() {
var data = processData();
$(onPageReady);
function processData() {
}
function onPageReady() {
if (!data.ready) {
// Wait for it to be ready
setTimeout(onPageReady, 0); // 0 = As soon as possible, you may want a
// longer delay depending on what `processData`
// is waiting for
return;
}
}
})();
Note that I happily use data in the onPageReady function, because I know that it's there; that function will not run until processData has returned. But I'm assuming processData is returning an object that is slowly being filled in via ajax calls, so I've used a ready flag on the object that will get set when all the data is ready.
If you can change processData, there's a better solution: Have processData trigger the ready handler when it's done. Here's the code for when processData is done with what it needs to do:
$(onPageReady);
That works because if the DOM isn't ready yet, that just schedules the call. If the DOM is already ready, jQuery will call your function immediately. This prevents the messy looping above.
I've got a sequence of Javascript function calls in a function I have defined to be executed when a web doc is ready. I expected them to be executed in sequence, as one ends the next begins, but the behaviour I see doesn't match up with that.
Additionally there is manipulation of the graphical components going on in between the calls (for example, I add in a checkpoint time to draw on a div on the page inbetween each of the mentioned calls) but those redraws aren't happening in sequence... they all happen at once.
I'm a bit of a n00b with the whole javascript-in-the-browser thing, is there an obvious mistake I'm making, or a good resource to go find out how to do this stuff?
Update - sample
// called onReady()
function init() {
doFirstThing();
updateDisplayForFirstThing();
doSecondThingWithAjaxCall();
updateDisplayForSecondThing();
...
reportAllLoaded();
}
IE won't update the display until the current script is finished running. If you want to redraw in the middle of a sequence of events, you'll have to break your script up using timeouts.
If you post some code we can help refactor it.
edit: here's a general pattern to follow.
function init() {
doFirstThing();
updateDisplayForFirstThing();
}
function updateDisplayForFirstThing() {
// existing code
...
// prepare next sequence
var nextFn = function() {
// does this method run async? if so you'll have to
// call updateDisplayForSecondThing as a callback method for the
// ajax call rather than calling it inline here.
doSecondThingWithAjaxCall();
updateDisplayForSecondThing();
}
setTimeout(nextFn, 0);
}
function updateDisplayForSecondThing() {
// existing code
...
// prepare next sequence
var nextFn = function() {
// continue the pattern
// or if you're done call the last method
reportAllLoaded();
}
setTimeout(nextFn, 0);
}
This can be fixed for many cases by using callbacks, especially with AJAX calls -- for example:
function doFirstThing(fn){
// doing stuff
if(typeof fn == 'function') fn();
}
function updateDisplayForFirstThing(){
// doing other stuff
}
function init(){
doFirstThing(updateDisplayForFirstThing);
}
Another option is to use return values:
function doFirstThing(fn){
// doing stuff
if(x) return true;
else return false;
}
function updateDisplayForFirstThing(){
// doing other stuff
return true;
}
function init(){
if(doFirstThing()){ updateDisplayForFirstThing(); }
}
setting timeouts to step through your code is not really a good way to fix this problem because you'd have to set your timeouts for the maximum length of time each piece of code could possibly take to execute.
However, you may still sometimes need to use a setTimeout to ensure the DOM has properly updated after certain actions.
If you end up deciding that you would like some JavaScript threading, check out the still being drafted Web Workers API. Browser support is hit and miss though the API is implemented in most modern web browsers.
Question: exactly how did you go about determining when the "doc is ready"? The DOMContentLoaded event isn't supported in IE I'm fairly certain... if you're in need of waiting for your document to load in its entirety you could use something like this:
var onReady = function(callback) {
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", callback, false);
return true;
} else if (document.attachEvent) {
var DOMContentLoaded = function() {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", DOMContentLoaded);
onReady();
}
};
return true;
}
};
Then of course you'll need to develop a setTimeout testing for some flags state indicating the page is loaded upon completion before continuing the execution of the rest of your code... that or any number of other methods...
Or you could just include the script at the bottom of your body...
I'm just rambling though until you have some code to show us?