2

I know there are tons of questions and answers on using Javascript promises to load returned Firebase objects, but I haven't succeeded in capturing the data in an array.

The issue: I've set up an array (productArray below) to iteratively take in values of a Firebase snapshot, but the array remains empty after exiting the forEach loop.

database.js:

  getProductList = function(){

    let productArray =[];

    loadFBData = function(){
      firebase.initializeApp(config);
      return firebase.database().ref().once("value", function(snapshot){
        return snapshot.val();
    });
  }

  Promise.all([loadFBData()]).then(function(snapshot) {
    snapshot.forEach(function(product) {
      productArray.push(product.val());
    });
  });
}

Question: I think I'm using Promise.all wrong, but can't figure out how (also am new asynchronous functions). Thanks for any hints on how to get productArray to successfully load.

Edit: To elaborate, it seems snapshot is loading with the correct Firebase data, and the forEach is correctly looping through each item in snapshot but productArray becomes (or remains) empty outside of the Promise.all statement.

Update I implemented all the suggestions below and yet, the program will not stop long enough to build an array from the snapshot object. Last year I used Angularfire's $loaded function which was so effective in getting the program to wait so I don't understand why an ordinary JS promise won't work here. Does it make sense to resort to timeout? I even tried to combine alexmac's and Pointy's suggestions, but no cigar:

 getProductList = function(){

    let productArray =[];

    loadFBData = function(){
      firebase.initializeApp(config);

      return new Promise(resolve => {
         firebase.database().ref().once('value')
            .then(function(snapshot){
               return resolve(function(snapshot){
                  productArray = Object.keys(snapshot).map((key, prod) => prod);
               });
            });
         });
   }

  loadFBData();
}
6
  • At first glance this code looks fine. It will only uses one promise, since you're loading the entire database. But that should work, unless I'm overlooking something. What's the problem you get with this code? Commented Aug 21, 2017 at 17:51
  • @Frank, thanks for responding. To elaborate, snapshot is indeed loading with Firebase data. The issue is (or must be) in that forEach loop, since (right after Promise.all and inside of getProductList()), productArray.length returns 0. Commented Aug 21, 2017 at 17:59
  • 1
    Your code isn't actually making a Promise at all. The loadFBData() function needs to return a new Promise with the current code inside the Promise callback. The function should pass the data retrieved to the resolve() function. Commented Aug 21, 2017 at 18:10
  • Why do you use Promise.all only for one element? Also if firebase.database().ref().once returns a promise, you don't need Promise.all at all. Commented Aug 21, 2017 at 18:42
  • @alexmac - you are right, I also just figured this out (having copied this solution originally from another SO answer - without understanding this usage. Commented Aug 21, 2017 at 18:46

2 Answers 2

1

Background

I think you're missing an understanding of both promises and the Firebase SDK.

First, note that all Firebase SDK functions return promises, so there's no need to create your own. Second, Promise.all is for combining multiple promises. A single promises can be chained off of with a simple .then().

I have a video on saving and querying data here: Save and Query Firebase Data

I have another video on promises here: Promises

Loading Firebase

You should be initializing firebase at the very top of your page. It only needs to be initialized once, so don't initialize it within a function.

You can call firebase.initializeApp(config) just after loading the Firebase SDK on the page. Or you can reference Firebase Hosting's init.js as shown below.

Example

The following example loads the Firebase SDK, uses my testing db's init.js file to initialize firebase, then queries, manipulates and returns an array.

Note that Firebase doesn't support zero-indexed arrays, so everything you get back from Firebase will be an unsorted object. This example shows a few different ways of returning the data :)

<html>
  <head>
    <script src="https://www.gstatic.com/firebasejs/4.3.0/firebase.js"></script>
    <script src="https://quiver-two.firebaseapp.com/__/firebase/init.js"></script>    
    <script>
      function loadFirebaseArray(path) {
        return firebase.database().ref(path).once('value').then(snap => snap.val());
      }

      function toArray(items) {
        return Object.keys(items).reduce((array, key) => {
          const value = items[key];
          array.push({key, value});
          return array;
        }, []).sort();
      }

      loadFirebaseArray('stackoverflow').then(items => {
        console.log('items', items);

        const asArray = toArray(items);
        alert(JSON.stringify(asArray));

        const justTheValues = asArray.map(x => x.value).sort();
        alert(justTheValues);
      });
    </script>
  </head>

</html>
Sign up to request clarification or add additional context in comments.

Comments

0

A working example might look like this:

getProductList = () => {
  let loadFBData = () => {
    firebase.initializeApp(config);
    return new Promise(resolve => {
      firebase.database().ref().once('value', resolve);
    });
  }

  let productArray =[];
  loadFBData()
    .then(snapshot => {
      productArray = Object.keys(snapshot).map((key, prod) => prod);
}

Here, I use promisification to create a promise for firebase query. So loadFBData returns a promise, I can create a promise chain or use ES7 async/await to wait until promise will be fulfilled to use result.

7 Comments

thanks I'm currently trying the ES6 solution and am trying to work through why I'm getting Uncaught (in promise) TypeError: snapshot.map is not a function
Add breakpoint or console.log(snapshot) before productArray = snapshot and check what is snapshot. According your code in question it must be an array.
snapshot is populated with a JSON object that looks to be in typical Firebase format: {"key": {...}, "key": {...}, ... }. Where key is (in my case) a string representing an integer. I was wondering if somehow the object is too deep but in fact only has 2 levels: key and {obj}. Anyway snapshot is an object not an array.
I'm trying to iterate through each item in the snapshot object and add/push these to an array. But if you say that the retrieved snapshot should be an array to begin with, then I'm probably dealing with an entirely different can of worms here.
Updated the answer, to iterate through snapshot properties.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.