Retrieving objects from Firebase in a loop using promises

Seeking guidance on handling promises in AngularJS as a newcomer. Struggling with merging data from two asynchronous arrays into a single array within a for-loop. Encountering a bug where the same picture is displayed for all entries despite different user data. The issue seems to lie in the code snippet below:

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

The challenge is apparent, but solutions like multiple deferred variables and $q.all haven't provided a clear path forward. Your input is valued, thank you :)

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;

}

Answer №1

There is a clear issue within this particular piece of code.

userPromise.then(function(user){
    picPromise.then(function(url){

The problem lies in the nesting of promises, which does not ensure that userPromise will resolve before picPromise.

These are two separate asynchronous calls. If picPromise resolves first, the subsequent code will not execute.

newfriendsinfo.push({
    id: newfriendid,
    name: user.val().name,
    email: user.val().email,
    agreed: newfriendagreed,
    profilepicture: url
});

Additionally, even if userPromise resolves prior to picPromise, issues will persist. The variables newfriendid and newfriendagreed are utilized within a promise created outside of the cycle, resulting in a closure problem.

Here's what occurs when the asyncUpdateFriendsInfo function is invoked.

Upon completion of the for loop, all requests are made (though responses have yet to be received) and newfriendid and newfriendagreed point to the last record's $id and agreed within newfriends. Consequently, all newfriendids within newfriendsinfo will be identical and correspond to the final newfriendid.

Refer to this resource regarding "Asynchronous Process inside a javascript for loop" for more insights.

It's advisable to substitute the existing code with something like

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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Inquiring about the rationale behind using `jquery.ui.all.css` specifically

I'm curious about the impact of jquery.ui.all.css on Tabs As soon as I remove it, <link rel="stylesheet" href="jquery/dev/themes/base/jquery.ui.all.css"> My tabs stop functioning. Here's my query: What exactly does the jquery.ui.all.cs ...

Experiencing a problem with value formatting while attempting to implement tremor for charts in React with Next.js version 13

import { getAuthSession } from "@/lib/auth"; import { db } from "@/lib/db"; import { Card, LineChart, Text, Title } from "@tremor/react"; import Linechart from "./LineChart"; const dollarFormatter = (value: number) ...

Navigating URLs to Single Page Application Routing in Vue.js

I'm new to using vue-router and I'm curious if there's a way to manage redirection in Vue or if there are alternative methods in a node.js environment. For instance, when someone tries to access my site by typing the URL example.com/contac ...

Check to see if the event handler is triggered and the promises are executed in sequence (syncronously)

I have a Vue button click handler that, depending on the arguments it receives, can do the following: execute request A only execute request B only execute request A and then request B sequentially (request B is only called if request A completes successf ...

Can you explain the meaning of this PHP syntax and why is this variable showing NaN?

Looking for question marks on Google can be quite challenging. Can someone explain this process step by step? $page = isset($_POST['page'])?$_POST['page']:"0"; I believe it means to check if $_POST['page'] is set, use that ...

removing the mapStateToProps function will result in an undefined value

I am new to React and I'm in the process of converting a class component to functional components using hooks. I need some guidance on safely removing 'mapStateToProps' without encountering undefined errors. I have two pages, A.js and B.js. ...

Here's a new take on the topic: "Implementing image change functionality for a specific div in Angular 8 using data from a loop"

I need to create a list with dynamic data using a loop. When I click on any item in the list, I want the image associated with that item to change to a second image (dummyimage.com/300.png/09f/fff) to indicate it's active. This change should persist e ...

Strip the CSS styling from an image (the CSS being included in the generated code)

I need to remove the CSS styling from an image, but I can't modify the generated code where it's coming from. My actual code looks like this: <div class="bubble"> <img id="one" src="/static/webupload/MyronArtifacts/images/descarga.png ...

Error: The parent selector "&" cannot be used in top-level selectors. $::webkit-input-placeholder

I am facing an issue while trying to run a legacy create-react-app that utilizes Sass. Initially, when I ran npm start, I encountered the error 'Cannot find module sass', which resembled the message found in this stack overflow post. To resolve t ...

Concealing a button once the collapse feature is toggled in Bootstrap

The following code snippet from the bootstrap website demonstrates how to use collapse functionality: <a class="btn btn-primary" data-toggle="collapse" href="#collapseExample" aria-expanded="false" aria-controls="collapseExample"> Link with href & ...

Attempting to transfer a newly created element from one location to another within the document after it has finished

I'm currently updating the design of my website. The currency selector app by Shopify is placed at the bottom, which has caused confusion for my international customers. To resolve this issue, I want to move it to a specific div class called .CSPositi ...

Upon attempting to fetch input by name, Puppeteer reported the error message: 'Node is not clickable or not an HTMLElement'

This is the structure of my HTML: <div id="divImporte"> <p class="btn01"> <input type="button" name="Enviar Tasas" value="Enviar Tasas"> </p> </div> Here are the diffe ...

Retrieving the chosen item from an ng-repeat list within a datalist

Currently, I am utilizing array iteration to identify the selected option based on the ID attribute of the result. Is there a more efficient approach for this task? <input type="text" list="products" ng-model="query" /> <datalist id="products"&g ...

Unable to get Discord.js sample code functioning correctly

Despite my best efforts, I can't seem to figure out why this simple example code is not working. As a newcomer to Java Script, I am struggling with understanding why the line GatewayIntentBits.Guilds is causing an error. Surprisingly, when I comment o ...

Dealing with AngularJS: Issue arises when attempting to inject $modal into a controller nested within a directive

Our team has implemented a custom directive that wraps around a checkbox and utilizes transclusion to inject content into it. Here is an example of the setup: somecheckbox.js angular.module('namespace.directives') .directive('someCheckbox& ...

Transforming JSON dates to Javascript dates using AngularJS and ASP.NET

How can I convert a JSON date /Date(1454351400000)/ into a proper date format using AngularJS and ASP.NET? $http.get('/home/GetProducts') .success(function (result) { $scope.products = result; }) .error(function (data) { ...

Incorporate a widget with dynamic height adjustment

We are currently working on creating a widget that can be easily embedded by third-party websites. Our goal is to have the widget automatically adjust its height through the embed script. Initially, we considered using an IFrame generated by our JavaScrip ...

When trying to implement appDir and withPWA in next.config.js, an error has been encountered

My next.config.js is set up with next-pwa and an experimental app feature included. const withPWA = require('next-pwa'); module.exports = withPWA({ pwa: { dest: 'public', disable: process.env.NODE_ENV === 'development&ap ...

What causes variations in running identical code between the Node environment and the Chrome console?

let myName = "World"; function functionA() { let myName = "FunctionA"; return function() { console.log(this.myName); } } functionA()(); Executing the code above in my terminal with node results in undefined, while running it in Chrom ...

Searching by multiple parameters in Angular.js

I have a script that scans through a field and highlights the words related to the search input. I want this script to not only filter by title, but also by name. How can I adjust the code to filter both the title and name when searching? <li ng-repeat ...