Reputation: 143
I need some help with promises if possible. I'm new to AngularJS and can't figure out how to deal with the problem I'm having. Basically, the issue is that I have two different arrays that are initialized asynchronously, while ultimately storing both data in the same array and doing this in a for-loop for multiple items. The bug I'm encountering is that while it loads the correct amount of items, it shows all of the different user data, but not a different picture. The picture that is shown is the same for each entry. At the end of the entire code section you find this:
userPromise.then(function(user){
picPromise.then(function(url){
newfriendsinfo.push({
id: newfriendid,
name: user.val().name,
email: user.val().email,
agreed: newfriendagreed,
profilepicture: url
});
}).then(function(){
if (newfriendsinfo.length == newfriends.length){
deferred.resolve(newfriendsinfo);
}
});
});
I am quite sure this is where my problem is. It looks for new user data while not using a new picture. However, I am not sure how I can deal with this problem. I have looked into multiple deferred variables, and $q.all, but I can't quite see how I'm supposed to tackle the issue. Below you find the entire relevant code. Thanks a lot for any help :)
var friendsRef = firebase.database().ref('friendships/' + firebase.auth().currentUser.uid);
$scope.friends = $firebaseArray(friendsRef);
$scope.friendsinfo = [];
$scope.$watch('friends', function() {
var newfriends = $scope.friends;
asyncUpdateFriendsInfo(newfriends).then(function(newlist){
$scope.friendsinfo = newlist;
});
}, true);
function fetchPicture(ref){
return ref.getDownloadURL().then(function(url) {
return url;
}).catch(function(error) {
alert("error");
});
}
function fetchUserInfo(ref){
return ref.once('value', function(snapshot){
}).then(function(snapshot){
return snapshot;
});
}
function asyncUpdateFriendsInfo(newfriends){
var deferred = $q.defer();
var newfriendsinfo = [];
for(var i = 0; i < newfriends.length; i++){
var ref = firebase.database().ref('users/' + newfriends[i].$id);
var profilePicRef = firebase.storage().ref("profilepictures/" + newfriends[i].$id + "/profilepicture");
var userPromise = fetchUserInfo(ref);
var picPromise = fetchPicture(profilePicRef);
var newfriendid = newfriends[i].$id;
var newfriendagreed = newfriends[i].agreed;
userPromise.then(function(user){
picPromise.then(function(url){
newfriendsinfo.push({
id: newfriendid,
name: user.val().name,
email: user.val().email,
agreed: newfriendagreed,
profilepicture: url
});
}).then(function(){
if (newfriendsinfo.length == newfriends.length){
deferred.resolve(newfriendsinfo);
}
});
});
}
return deferred.promise;
}
Upvotes: 0
Views: 540
Reputation: 1167
The problem is definitely in this code.
userPromise.then(function(user){
picPromise.then(function(url){
You have nested promises and this does not guarantee that userPromise
will be resolved first and picPromise
will be resolved second.
They are two independent asynchronous calls. In case if picPromise
is resolved first the following code will never be called.
newfriendsinfo.push({
id: newfriendid,
name: user.val().name,
email: user.val().email,
agreed: newfriendagreed,
profilepicture: url
});
Besides that, even if first userPromise
is resolved and then picPromise
you will still have problems.
You are using variables newfriendid
and newfriendagreed
in promise which is created outside of promise in cycle. Here you have problem of Closures.
Here is what will happen when asyncUpdateFriendsInfo
function is called.
When for cycle is finished all request will be sent (but responses are not received yet) and newfriendid
and newfriendagreed
point to newfriends
's last record's $id
and agreed
. So in newfriendsinfo
all newfriendid
will be the same and will be last newfriendid
.
Check out this question for "Asynchronous Process inside a javascript for loop"
If fact you should replace this code
userPromise.then(function(user){
picPromise.then(function(url){
newfriendsinfo.push({
id: newfriendid,
name: user.val().name,
email: user.val().email,
agreed: newfriendagreed,
profilepicture: url
});
}).then(function(){
if (newfriendsinfo.length == newfriends.length){
deferred.resolve(newfriendsinfo);
}
});
});
into something like this
(function(newfriendid){
var finalUser,
finalUrl;
userPromise.then(function(user){
finalUser = user;
checkIfBothLoaded();
});
picPromise.then(function(url){
finalUrl = url;
checkIfBothLoaded();
});
function checkIfBothLoaded(){
if (finalUser && finalUrl){
newfriendsinfo.push({
id: newfriendid,
name: finalUser.val().name,
email: finalUser.val().email,
agreed: newfriendagreed,
profilepicture: finalUrl
});
}
if (newfriendsinfo.length == newfriends.length){
deferred.resolve(newfriendsinfo);
}
}
})(newfriendid, newfriendagreed);
Upvotes: 1