Consider this example of pretty standard method in Angular Js, which updates the view:
$scope.fetchResults = function() {
// Some local variable that will cause creation of closure
var hugeData = serviceX.getMilionRecords();
// Any call to any resource with success and error handlers.
$http({
method: "GET",
url: "/rest-api/bulk-operation-x",
params: { someParam: hugeData.length }
}).success( function () {
var length = hugeData.length;
$scope.reportToUser("Success, that was " + length + " records being processed!";
}).error( function () {
var length = hugeData.length;
$scope.reportToUser("Something went wrong while processing " + length + " records... :-(";
});
};
This is of course hypothetical example, but it nicely shows pattern, which could be described as reusing of local variables from within AJAX callbacks.
Of course in both handlers (success and error) we are creating a closure over hugeData which is directly referenced from callback handlers.
My question is: since the result of AJAX call can be only either success or failure, will reusing of this code cause the memory leak over time? I would answer "yes", but I couldn't reliably prove this one in my local tests.
I'd like some more experienced guru to explain this one for me. I'd love response from anyone working with Angular on daily basis, but any jquery responses are welcome as well.
You will have a memory leak as soon as you return the result of $http() call (or whatever object or function that has access to hugeData) into the outer scope of fetchResults.
With your code, nothing big is exposed directly outside of fetchResults, and the result of $http() call will live until it either succeeds or fail, then calling the corresponding callback, finally getting GC'ed.
See for insights: http://jibbering.com/faq/notes/closures/#clIdRes
As #ŁukaszBachman observes, this does not guarantee that there are no memory leaks. Any dangling reference to your big object or to your callback with big object in scope, will cause woe.
So, let's check into $q implementation ($http is based on $q).
If you check https://github.com/angular/angular.js/blob/master/src/ng/q.js#L191, you can see that the resolve() method of the deferred first copies the list of registered callbacks in a variable local to the method:
var callbacks = pending;
subsequently nullifies the external pending (that was defined at the defer level)
pending = undefined;
then, at next tick, executes the callbacks. Things may get complicated by the fact that the callback's argument may be a deferred itself (adding a further delay to execution), but at most you could get into an infinite loop. (And that's not funny!). If you are lucky enough not to get into the loop, then at some point the callback array is exhausted, and then there is no reference whatsoever to the callback list, so it's available for GC.
But.
Things may go wrong if you force them to.
You may use arguments.callee inside a callback.
You can throw beer on your keyboard too.
If you jump out of the window, unless you live on the first floor, you will probably get hurt.
Happy EcmaScripting!
Related
So, I wrote the following function:
function getData() {
var data;
$(function () {
$.getJSON('https://ipinfo.io', function (ipinfo) {
data = ipinfo;
console.log(data);
})
})
console.log(data);
}
The problem with the above is the 2nd console.log doesn't retain the info from the assignment inside the jQuery and logs an undefined object. I'm not exactly sure what is wrong, but I believe it to be something quite minor. However, as much as I've searched online, I haven't found an answer for this particular problem.
One line: Javascript is Asynchronous.
While many struggle to figure out what it exactly means, a simple example could possibly explain you that.
You request some data from a URL.
When the data from second URL is received, you wish to set a variable with the received data.
You wish to use this outside the request function's callback (after making the request).
For a conventional programmer, it is very hard to grasp that the order of execution in case of JavaScript will not be 1,2 and then 3 but rather 1,3,2.
Why this happens is because of Javascript's event-loop mechanism where each asynchronous action is tied with an event and callbacks are called only when the event occurs. Meanwhile, the code outside the callback function executes without holding on for the event to actually occur.
In your case:
var data;
$(function () {
$.getJSON('https://ipinfo.io', function (ipinfo) {//async function's callback
data = ipinfo;
console.log(data);//first console output
})
})
console.log(data);//second console output
While the async function's callback is executed when the data is received from the $.getJSON function, javascript proceeds further without waiting for the callback to assign the value to the data variable, causing you to log undefined in the console (which is the value of the data variable when you call console.log.
I hope I was able to explain that.!
I concede that, despite hours of reading and attempting, I am fundamentally unable to grasp something about Deferred promises and asynchrony in general.
The goal on my end is real, real simple: send some data to the server, and react to the contents of the response conditionally.
The response will always be a JSON object with save and error keys:
{ "save": true, "error":false}
// or
{ "save" : false,
"error" : "The server has run off again; authorities have been notifed."}
I have tried dozens and dozens of variations from the jQuery API, from other stackexchange answers, from tutorials, etc.. The examples all seem concerned with local asynchronous activity. When I need is some ability to be made aware when the AJAX request has either finished and returned a response I can inspect and make decisions about, or else to know that it's failed. Below, I've used comments to explain what I think is happening so someone can show me where I'm failing.
I know this is a repost; I am, apprently, worse than on average at grasping this.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
/* when(), as I understand it, should fire an event to be
responded to by then() when it's contents have run their course */
var result = $.when(
/* here I believe I'm supposed to assert what must complete
before the when() event has fired and before any chained
functions are subsequently called */
/* this should return a jqXHR object to then(), which is,
I'd thought, a queue of functions to call, in order,
UPON COMPLETION of the asynchronous bit */
$.post("my/restful/url", postData))
.then( function() {
/* since "this" is the jqXHR object generated in the $.post()
call above, and since it's supposed to be completed by now,
it's data key should be populated by the server's response—right? */
return this.data;
});
// alas, it isn't
console.log(result.data);
// >> undefined
Most examples I can find discuss a timeout function; but this seems, as I understand, to be a failsafe put in place to arbitrarily decide when the asynchronous part is said to have failed, rather than a means of stalling for time so the request can complete. Indeed, if all we can do is just wait it out, how's that any different from a synchronous request?
I'll even take links to a new read-mes, tutorials, etc. if they cover the material in a different way, use something other than modified examples from the jQuery API, or otherwise help this drooling idiot through the asynchronous mirk; here's where I've been reading to date:
jQuery API: Deferred
JQuery Fundamentals
jQuery Deferreds promises asynchronous bliss (blog)
StackOverflow: timeout for function (jQuery)
Update
This is in response to #Kevin B below:
I tried this:
var moduleA = {
var moduleB = {
postData: {"id":7, "answer":"Ever since I went to Disneyland..."};
save: function() {
return $.post("path/to/service", postData, null, "JSON");
}
};
var result = this.moduleB.save();
result.done(function(resp) {
if (resp.saved == true) {
// never reached before completion
console.log("yahoo");
} else {
console.log("Error: " + resp.error);
// >> undefined
}
});
}
You are over-complicating your code. You cannot get the data to outside of the callback, no matter how many deferred/promises you create/use (your sample creates 3 different deferred objects!)
Use the done callback.
var postData = {"id":7, "answer":"Ever since I went to Disneyland..."};
$.post("my/restful/url", postData).done(function (result) {
console.log(result.save, result.error);
});
You seem to have a misunderstanding of both asynchronous requests, the Promise pattern, and Javascripts mechanism of passing functions as an argument.
To understand what's really happening in your code I suggest you use a debugger and set some breakpoints in the code. Or, alternatively, add some console.logs in your code. This way you can see the flow of the program and might understand it better. Also be sure to log the arguments of the function you pass as an argument in the then()-method, so you understand what is passed.
ok you got it half right. the problem is that when you execute the console.log the promised is not yet fulfilled the asynchronous nature of the promises allows the code to execute before that ajax operation is done. also result is a deferred not a value, you need to handle your promised with .done instead of .then if you wish to return a value otherwise you'll continue passing promises.
so that said
var result={};
$.when(
$.post("my/restful/url", postData))
.done( function(data) {
result.data=data;
});
// here result is an object and data is a undefined since the promised has no yet been resolve.
console.log(result.data);
I'm not even sure how to phrase this question so I'll have to go with examples. This might not look like useful code here, and indeed it isn't, but it's an example of a problem I've just encountered, stripped down to the bare essentials.
Lets' assume I have
function myObject(params) {
.. do some time-consuming asynchronous stuff
with AJAX and the like ...
return (before all the above is completed);
}
function myFunction(params) {
var doTheSlowStuff = new myObject(params);
}
myFunction(firstParams);
myFunction(moreParams);
What happens to the first myObject when I make the second call to myFunction()? Does it get a chance to complete its work (and if so will it be garbage collected when it has)? Or does it get unceremoniously dumped before it has a chance to finish what it started?
All the time-consuming asynchronous stuff will happen asynchronously :)
That means that the async calls (such as XHR or setTimeout) return instantly and allow execution to continue. In other words, the myObject constructor will return very quickly so there will be no delay between constructing the two myObjects. After both myFunctions return, then finally control will return to the event loop and the JavaScript engine will continue processing events, like mouse clicks, WebSocket events, timers like setTimeout, or XHR requests coming back. Your asynchronous callbacks won't be executed until you return control to the event loop, so don't do anything crazy like
while(true) {
// Check XHR status
}
Don't worry about garbage collection; if you have a DOM event like an AJAX (XHR) request with your myObject in scope then it won't be garbage collected until the event handler itself is garbage collected.
In this specific instance, you will create two instances of myObject which will be retained in memory until your application exits.
You can prove this by running something asynchronously to test this behaviour:
function myObject(params) {
// do something async, like output
// every second ...
var callback = function () {
console.log("I am object " + params);
setTimeout(callback, 1000);
};
callback ();
}
function myFunction(params) {
var doTheSlowStuff = new myObject(params);
}
myFunction(1);
myFunction(2);
// etc.
See a working example at: http://jsbin.com/osEFuWib/1/edit
The asynchronous code has access to a callback function. The callback will run and is independent of any other object. "Losing" the object or creating a new object does not change this. As such, pending asynchronous operations must be explicitly cancelled or the callbacks must be guarded against performing unwanted effects when they are invoked.
The thing about objects in JavaScript is simple: as long as any code - including the callback - can access the object (e.g. assigned to a variable in scope, a window property, or bound to the DOM), they remain accessible. Otherwise, they are unreachable and will be reclaimed at some point.
Just as I thought I understood JS scope...
Take this code:
function init() {
var page;
var pageName = $('body').data('page');
switch(pageName) {
// (there are more switch cases here normally...)
case 'pSearchLatest':
require(['searchresults'], function (SearchResults) {
page = new SearchResults().init();
console.log(page); // <- shows the object!
});
default:
break;
}
console.log(page); // <- undefined
return page;
}
See the console log comments. Why is the second returning undefined when the var page is declared outside of the scope of the switch statement?
EDIT
So I mistakenly thought this was a scope issue but it's down to the asynchronous nature of AMD.
How can I return the value of page in the require scope in the same method without a while loop checking for undefined?
EDIT 2
I did it like this:
function init(callback) {
case 'pSearchLatest':
require(['searchresults'], function (SearchResults) {
page = new SearchResults().init();
callback(page)
});
}
and in my page that calls the wrapping init() method:
new PageController().init(function(asyncViewController) {
App.view = asyncViewController;
});
You did understand scope correctly. The code in that inner function does indeed assign to the variable in the outer scope.
What you did not understand is asynchronous behaviour. The require function takes a callback function which will be invoked somewhen in the future - when the requested module is available. Yet, it does immediately return and control flow will lead to the console.log statement, which does print undefined - which is the value the page variable has at that time.
You should be able to recognise that since the undefined is logged first, and the log statement in the callback (with the object) executes later, even though it comes above in the code.
require is asynchronous, therefore your function did exit first and then processed callback function.
Two possible reasons... you didnt match the case. or the callback where the assignment of the value to page happens hasnt been executed yet. Im guessing its the latter since it would be readily apparent if you case was failing. Look into the documentation for whatever AMD library youre using and see how the execution of the function(s) passed to require are executed.
Also as a generaly rule try to avoid returning values determined by asynchronous calls like this. Instead use some kind of Observer pattern to subscribe to a particular event that can be triggered from different places.
See the following line of code:
WinJS.xhr({ url: "http://someurl.com" }).then(
function fulfilled(result)
{
if (result.status === 200)
{
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
});
As far as I understand, when WinJS.xhr has completed whatever it does then execute the anonymous function 'fulfilled' with argument 'result'
Coming from Java/C++ background, I'm extremely confused with how this code works - how is 'result' being passed to this function? Where does it say anything about what 'result' is? How can I know what type of object 'result' is and how it has a 'status' member?
I'm going to break my answer into two parts: The first concerns the actual execution model of Javascript, and the second which concerns the high-level expression as written.
The Javascript Execution Model
WinJS evaluates to an object.
That object has a prototype which contains an xhr member which WinJS.xhr evaluates to. That member is a function, which we will refer to as A below so that we can keep clear what exactly is going on.
Before we get to that, { url: "http://someurl.com" } returns an object which we will refer to as B.
That object B has a property called url.
A(B) calls a function A with a value B as an argument. It returns an object that we will refer to as C.
That object C has a prototype which contains a member named then. C.then happens to evaluate to a function. That function we will refer to as D.
function fulfilled(result) {...} returns a function that we will refer to as E. It can also be referred to as fulfilled but that fact is not used in this program fragment.
D(E) calls a function D with a value E as an argument. Nothing is done with the return value.
The high-level view
There are three functions here; one is a callback (called fulfilled), and the other two may be called "methods"- one xhr of the WinJS global object, and then of a promise object.
WinJS.xhr({ url: "http://someurl.com" }) creates and returns that promise object. You can convince yourself of this by consulting the documentation.
The promise object has a method called then which registers what you can think of as an event handler for when the promise is done. The value result - used in that callback registered in then comes from whatever is making that promise done by in fact calling the method done on that promise. You don't see the code that does that because it's someplace in the implementation of WinJS.xhr.
What WinJS.xhr is doing is performing a network request. When that network request is done it will signal the result of that network request (which according to the documentation is an XMLHttpRequest object) through the promise by calling the done() method on that promise. That in-turn calls the callback we registered with the then() method.
The documentation for the WinJS functions is still not great, IMO. You can look at the documentation for WinJS.xhr at http://msdn.microsoft.com/en-us/library/windows/apps/br229787.aspx, and that will give you some information on that - it says that the xhr function "wraps ... XMLHttpRequest object in a promise". Personally I've found it easier to look at the examples / quickstarts than at the reference documentations.
What you have passed to the fulfilled function (or the first function there) isn't the "result" of the operation, but the XMLHttpRequest object itself. On that you can get its properties to see the result - take a look at http://msdn.microsoft.com/en-us/library/windows/apps/ms535874.aspx for its reference.
The xhr method returns a Promise object, which has the then method that takes the onComplete, onError and onProcress callback functions.
This code only uses the onComplete callback. The callback is called when the request is completed, and it's sent a parameter with the value sent from the server.
If you are not familiar will callback functions, it may be clearer if you declare a regular function an use as the callback:
function fulfilled(result) {
if (result.status === 200) {
resDiv.style.backgroundColor = "lightGreen";
resDiv.innerText = "Success";
}
}
WinJS.xhr({ url: "http://someurl.com" }).then(fulfilled);
The then method is intended for pre-processing the result, you should rather use the done method to take care of the result.
I haven't found anything in the documentation that specifies exactly in what form the value comes from the server. It's probably there somewhere, but as usual with Microsoft documentation, it's rather complete but everything isn't everywhere so you have to look in different places to find specific information.