AngularJS - $scope variable not storing the data outside of the function - javascript

I've got a pretty simple function like this inside my controller:
fetchUsers = function () {
UserService.getUsers().
success(function(data, status) {
$scope.users = data.users;
console.log($scope.users);
});
};
fetchUsers();
console.log($scope.users);
It's basically using a service to retrieve some data from an API, and the console.log inside of the function is working fine and sends an array to console of [Object Object] which I can open up and see all my users no problem. However, when I then put this outside of the function by calling fetchUsers() and running a console.log I simply get an empty array returned where [] is output to the console.
Can anyone think of anything that would cause this? I'm a bit baffled by it.

Ahh I see you've finally been hit by asynchronous code of javascript. Not everything runs in order.
For example if I have this piece of code
setTimeout(function(){
var test = 'hello';
console.log('first test : ' + test);
}, 1000)
console.log('second test : ' + test);
You'll notice that the second test will return nothing even though test is set earlier (in terms of line number). Now you may think, so what you set it to 1000 milli-seconds (but try the same code and set it to 0 seconds, you will see the same effect. This is due to the event loop which is used to manage asynchronous code - basically whatever is in your setTimeout is placed at the end of the priority, which means when the second console log is called - test is not defined yet.
Why asynchronous you ask? The thing with browsers is UI and javascript is run on the same thread, so imagine if the setTimeout got stuck there and waited for 1 whole second, nothing in your UI would work - it would freeze.
With that said, another important usage of asynchronous code is http requests to a server. These requests can take a variant of time which means if you use synchronous code, your UI would freeze up. Imagine facebook - which is constantly retrieving data from its servers.
Going back to your code, this method below retrieves data from the server
UserService.getUsers().
success(function(data, status) {
$scope.users = data.users;
console.log($scope.users);
});
The stuff inside the success function is again asynchronous, so whatever you put after that will run straight away and whatever is inside the success function will run once your 'promise' or request has been fulfilled.
Hence you might have
fetchUsers();
console.log($scope.users);
But note that $scope.users is set after this console.log

UserService.getUsers() is returning a promise which will resolve in future as it is executed in async with your controller code so your console.log($scope.users) in your controller is most likely being executed before the API call returns and success callback is executed.
Put a breakpoint in browser developer tools on both console.log() statement and you should observe this behavior.
Here is a pretty simple explanation of promises in general

Your code is running properly. It is the asynchronous nature of the request that is causing the confusion. Because its asynchronous, the success callback is actually executed last, after your 2nd console.log() call.

Make $scope.users global by initializing it outside of the function perhaps? I also threw the function inside the getUsers method:
$scope.users = [];
fetchUsers = function () {
UserService.getUsers(function(data, status) {
$scope.users = data.users;
console.log($scope.users);
})
};
fetchUsers();
console.log($scope.users);

Related

How to use async:false in angular js 1.6, so that java script next line execution get block until $http completed

Currently, I am using angularjs 1.6, calling $http to initialize an array, and using that array after 1000 line of code in an angularjs controller, but some strange thing is happening every time. Sometimes I get array initialized properly and sometimes I don't.
Basically, I would like to call JavaScript code in certain order, like line1, line2, line3 etc, here line1 may call $http, line2 may call some function line3 again $http,
Even I have tried using
$http( { method:'POST',
url:'/getData',
async:false
})
But It's not holding execution JavaScript, instead, next line of code gets executed.
Is there any way to control this, so that I can execute the JavaScript code in the same order in the same way I have written,
First of all in many case moving to "none asynchrone" call is a bad idea. You don't know how long this call will take, and during this time you block the UI event loop, so nothing more is working and your website will be totaly stuck during this time, which is pretty a bad thing.
Depend of what you are using (ecma script / javascript) but async function seem to be what you are looking for : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Instructions/async_function
Exemple :
async function myFunction(){
var data = await $http.get(...);
await $http.post(..);
return "my data";
}
Everything in this function will be execute in the order it's define thanks to the keyword await but it's still asynchrone, the whole function will return a Promise.
myFunction().then(function(myData) {
console.log("End of the asynchrone function " + myData);
});

Variable scope with jQuery and javascript

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.!

JavaScript nested fetch API

I started working on an android app. I am at the point that i need to use an API to get some info. I am using an API for movies so it contains id, title and some other info. My problem is that in this URL i don't hav every information that i need so i have to make another fetch for every movie to get the additional info but as i noticed first JS runs the code in the first fetch ( without the nested fetch ) and then it runs the nested fetch. This way some info are not displayed on the screen.
fetch(movie)
.then(function(response) { return response.json(); })
.then(function(responseObject) {
movies.value = responseObject;
for(var i=0;i<movies.value.results.length;i++)
{
movies.value.results[i].poster_path = poster+movies.value.results[i].poster_path;
var info = "http://api.themoviedb.org/3/movie/"+movies.value.results[i].id+"?api_key";
fetch(info)
.then(function(response) { return response.json(); })
.then(function(responseObject) {
moreInfo.value = responseObject;
if(j < movies.value.results.length)
{
movies.value.results[j].runtime = moreInfo.value.runtime;
console.log("print 1 "+movies.value.results[j]);
j++;
}
});
console.log("print 2 "+movies.value.results);
}
});
what i am trying to do here is add from the second fetch the runtime to my movies Observable object. A sortened version of the result :
print 2 : movies.value.results
print 1 : movies.value.results[i]
The problem as i said is that the code inside the first fetch is executed without executing the nested fetch and then the nested fetch executes. Sorry if i did something really bad but i just started developing on android so please show me mercy :D
*Sorry for not explaining about my j variable. It is defined as zero above fetch(movie). The only reason i use it is because i inside fetch(info) is always at the max number i cang get from movie.value.results.length and because i need to pass the runtime at the specific movie.value.result[j]. So i have a counter that increases only when the fetch(info) is executed.
This snippet might have some other problems too, like pointed out in the comments, but I think I know where the main confusion comes here.
The problem as i said is that the code inside the first fetch is executed without executing the nested fetch and then the nested fetch executes.
This is not true. Or, at least the order of console logs doesn't imply that. The order of execution inside the outer then-callback is
fetch(info) This statement starts fetching the data
.then(function(response){ ... }) Register a callback to execute when the promise gets resolved. Note that as fetching is asynchronous, the callback function does not get called right away. It will get called later, when the data has finished loading. Meanwhile, synchronous execution of the call stack continues.
.then(function(responseObject) { ... }) Same thing here. We create a function to run when the data is ready (if ever). Meanwhile the statements outside that function can still be evaluated.
console.log("print 2 "+movies.value.results); Now here comes the first log statement. The JavaScript execution does not halt to wait for the fetch to finish. That's the whole idea of asynchronous programming.
Now, some time passes and finally, the data has finished loading.
return response.json(); The Promise returned from the fetch gets fulfilled because the operation was successful. It then runs the callback that you provided earlier.
moreInfo.value = responseObject;
if(j < movies.value.results.length)
{
movies.value.results[j].runtime = moreInfo.value.runtime;
console.log("print 1 "+movies.value.results[j]);
j++;
}
This is the second callback we created and same thing applies here. Now that the data is finally ready, this can be executed. I'm not sure about the whole j thing, but apparently the execution will eventually get to the console.log("print 1 "+movies.value.results[j]); statement.
The important thing to understand here is that whether or not the 'print 1' statement was in the source code above the 'print 2' line is irrelevant. The 'print 1' was scheduled for later, namely, when the second fetch is done.
Fixed version
var info = //URL
fetch(info)
.then(function(response) { return response.json(); })
.then(function(responseObject) {
moreInfo.value = responseObject;
...
console.log("print 1 "+movies.value.results[j]);
...
})
.then(function(){
console.log("print 2 "+movies.value.results);
});
Now it could also be that I totally misunderstood the question. But if this was about asynchronicity, you should also check out the MDN Promise docs linked above and this SO question about asynchronicity in Node.js, which also applies to other JavaScript environments.

Understanding jQuery.Deferred in the context of jQuey.AJAX (again)

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);

Extract Data from Anonymous Function Scope

Because of the complexity of this application, I have a need to wrap Facebook API calls, like so.
//In main file, read is always undefined
var read = fb_connect.readStream();
// In fb_wrapper.js
function readStream () {
var stream;
FB.api('/me/feed', {limit:10000}, function (response) {
stream = response.data;
});
return stream;
}
I know that due to the asynchronous nature of the call, the rest of the readStream() function will return stream (which has no value). I am having trouble finding a way of getting the data out of the callback function scope and back up to a higher scope. The FB API call is returning fine (I have debugged it a hundred times), but getting that response data has been the battle thus far.
If anyone has any suggestions, it would be much appreciated. I searched for Facebook jQuery plug-ins (as a pre-made wrapper, perhaps) with little luck.
Judging from your question, it seems that you are looking for a synchronous call. Which means that you'd want to use the data returned from the api call right after calling it. In that case, you'll need to check whether FB.api supports synchronous calls (mostly doesn't).
Otherwise, you'll need to understand that you are making an async call here. Which means that you should put your handling code INSIDE the callback function that you pass to FB.api. This is called the "continuation" style of writing code and is the standard way to use async calls.
FB.api('/me/feed', {limit:10000}, function (response) {
var stream = response.data;
// Do your processing here, not outside!!!
});
Or:
function handlerFunction(response) {
// Do your processing here
}
FB.api('/me/feed', {limit:10000}, handlerFunction);

Categories

Resources