Utilizing asynchronous calls within a forEach loop for executing Firebase operations

My dilemma lies in the inability to successfully implement this code using Firestore (although I'm not entirely sure if this is relevant).

The specific code snippet is as follows:

prestamoItems() {
      var myarray = [];
      var myobject = {};

      //Beginning of first async method (executes properly)

      fb.prestamosCollection
        .orderBy("fechaPrestamo", "desc")
        .get()
        .then(val => {
          if (!val.empty) {

            //Here begins the forEach loop

            val.docs.forEach(doc => {
              myobject = doc.data();
              myobject.id = doc.id;
              console.log("The doc id is " +myobject.id)

              //Second asynchronous call inside the forEach loop, but it doesn't wait for completion 
              //before moving on to the next step

              fb.equiposCollection.doc(myobject.id).get().then(eqp => {
                console.log("The doc id from the other collection is " +eqp.id)
              })

              myarray.push(myobject)
              console.log("myobject pushed to myarray")
            });


          }
        });
    }

I want to highlight that I have an asynchronous method being called within a forEach loop nested within another asynchronous method. Despite various iterations of the code structure, the console logs consistently display the following output:

11:13:14.999 Prestamos.vue?18d2:71 The doc id is 1yTCUKwBvlopXX2suvVu
11:13:14.999 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is Z5TE15Fj3HFrn1zvceGe
11:13:15.000 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is JNN9aN65XE1tUTmlzkoJ
11:13:15.000 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.000 Prestamos.vue?18d2:71 The doc id is NF2hHCpM8leZezHbmnJx
11:13:15.001 Prestamos.vue?18d2:78 myobject pushed to myarray
11:13:15.364 Prestamos.vue?18d2:74 The doc id from the other collection is 1yTCUKwBvlopXX2suvVu
11:13:15.368 Prestamos.vue?18d2:74 The doc id from the other collection is Z5TE15Fj3HFrn1zvceGe
11:13:15.374 Prestamos.vue?18d2:74 The doc id from the other collection is JNN9aN65XE1tUTmlzkoJ
11:13:15.379 Prestamos.vue?18d2:74 The doc id from the other collection is NF2hHCpM8leZezHbmnJx

Hence, the behavior demonstrates that the forEach loop does not pause for the inner asynchronous function to complete, which aligns with expectations.

In light of this, my question is how do I ensure the loop waits for the internal call to finish before appending the object to the array? Any insights would be greatly appreciated.

Answer №1

To incorporate code that relies on previous outcomes into then() callbacks, you can either nest the code or encapsulate the loop (since forEach doesn't support async operations) in an async block to utilize await within it. For example:

fb.prestamosCollection
  .orderBy("fechaPrestamo", "desc")
    .get()
    .then(val => {
      if (!val.empty) {
        // Enclose the loop within an async function call immediately invoked function expression (iife) to enable usage of await inside
        (async () => {
          for (var i = 0; i < val.docs.length; i++) {
            const doc = val.docs[i];
            myobject = doc.data();
            myobject.id = doc.id;
            // Execution will now be synchronous
            let eqp = await fb.equiposCollection.doc(myobject.id).get();
            console.log(eqp.id);
            myarray.push(myobject)
          }
        })();
      }
    });

Answer №2

The underlying issue stems from attempting to convert an asynchronous operation (waiting for Firestore to retrieve values) into a synchronous one. Accomplishing this in a meaningful manner within JavaScript poses significant challenges!

To resolve this, you must populate your array within the .then() callback and return the promise as the outcome of the function. Any invocation of your prestamoItems() function will also necessitate using .then() callbacks to access the underlying myarray value:

const _ = {
  async prestamoItems() {
    const val = await fb.prestamosCollection.orderBy("fechaPrestamo", "desc").get();
    if (val.empty) {
      return myarray
    }

    // Promise.all() aggregates multiple promises and returns their results upon completion.
    return await Promise.all(
      // Array.prototype.map() executes a function on each item in an array and gathers the returned values in a new array.
      // This is akin to building a new array by using push(), but it's more readable!
      val.docs.map(async doc => {
        const myobject = doc.data();
        const eqp = await fp.equiposCollection.doc(myobject.id).get()
        // Presumably, further actions involving eqp are intended here
        return myobject
      })
    );
  }
}

The provided code snippet employs Array.prototype.map(), rendering myarray superfluous.

A user would need to utilize this code as follows:

_.prestamoItems().then((myarray) => {
   ...
})

Promises signify that a value may become available at some future point. Accordingly, all interactions with a promise should assume the value isn't immediately accessible. The recommended approach involves employing async/await and ensuring the consistent usage of promise objects.

Answer №3

To properly execute the process, ensure to shift the push operation internally:

fb.equiposCollection.doc(myobject.id).get().then(eqp => {
   console.log("The doc id from the other collection is " +eqp.id)

   myarray.push(myobject)
   console.log("myobject successfully added to myarray")
})

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

The JQuery video player's full screen toggle feature is not correctly assigning the class name

I've been tackling a bug in my JavaScript/jQuery video player that has left me stumped. One of the key features of this player is an enter/exit full-screen button located at the bottom of the HTML snippet: (function($) { /* Helper functions */ ...

Retrieving environmental information within a Vue component

I am trying to display information from my .env file, specifically the APP_NAME, in my components. For example, I want to greet users with Welcome to {{APP_NAME}}. UPDATE After referring to this documentation, I have updated my env file as follows: MIX ...

How can I make certain options disabled in a multiselect list?

I am faced with a challenge of having 3 multiselect fields with the same set of options to choose from. <div> <ul class="nav nav-tabs"> <li class="active"><a data-toggle="tab" href="#tab1">TAB 1:</a></li> <li> ...

Using React-Testing-Library to Jest TestBed Hook in TypeScript for Jest Testing

I'm currently facing a challenge while attempting to integrate the react-hooks library with Formik, specifically using useFormikContext<FormTypeFields>() in TypeScript within my project. I have certain fields where I want to test the automation ...

When using jQuery, hover over an li element while ignoring its children

I'm working on customizing a Wordpress menu that has a submenu. I managed to add an arrow to the 'li' element that contains a submenu, and created a hover animation with CSS when the mouse moves over the 'a' tag inside the 'li ...

What is the best way to deactivate ng-repeat using a button in AngularJS?

How can I disable the apply button once it has been clicked by the user in a list of stores based on services? I am using ng-repeat to display the listing. Additionally, how can I write an apply function to disable a particular service once it has been app ...

My project is experiencing issues with the execution of JavaScript codes

Greetings, I'm having trouble with my JavaScript codes. The following code works perfectly: function Choice() { var box = document.getElementById('searchh'); if (!count) { var count = 0; } box.onclick = function () ...

Storing values of properties in variables for quick access in Javascript

I have a straightforward read-only JSON object with several properties. I am contemplating whether it would be beneficial to store these properties in variables for caching purposes. Currently, we access them using syntax like jsonObject.someProperty. Th ...

Detect the form submit event within a directive

In my directive, I am trying to listen for form submissions. This is what my directive currently looks like: app.directive('myDirective', function () { return { restrict: 'A', require: '^form', sco ...

Error in Bootstrap(v4) Popover: $(...).popover function not recognized

Hey there, I'm facing an issue with bootstrap popovers. Specifically, I keep receiving the error message: Uncaught TypeError: $(...).popover is not a function. I've come across similar problems and have taken the following steps: Checked that b ...

What could be causing json_decode to return NULL in this scenario?

I have a custom array that I've created with the following structure: [{ "assetPreviewUrl":"pic1.jpg", "assetUrl":"pic2.jpg" }, { "assetPreviewUrl":"pic3.jpg", "assetUrl":"pic4.jpg" }] My approach involves stringifying this array and ...

Removing text that was added via the chart renderer in Highcharts can be accomplished by identifying the specific element

Instead of using a legend in my graph, I have added labels directly to the series (view example here). After drawing the graph with these labels attached, the user can click a button to add another series which hides some existing lines successfully. Howev ...

Learn how to make a specific row blink twice with the power of Vue.js and Vuetify

Once a record is successfully created, I have implemented a feature where an alert message appears in green if the API returns 200 OK, and red if not. This functionality is currently working flawlessly thanks to my use of Vuex + Vuetify Snackbar. this.noti ...

Ignores transparent pixels when using Ray in Three.js

I am working with Vector3ds that contain plane geometries using transparent pngs for their materials. One issue I'm facing is that the Raycaster is detecting the whole object, so even clicking near the material triggers the corresponding functions. ...

Passing an array of selected values from Vue.js to a text area and adding them to the existing content

I'm facing a situation and I could really use some assistance. The image shows that there is a multiple select box on the left with numbers, and a text box on the right. My goal is to allow users to click on the house numbers in the select box and hav ...

The HTML table seems to be inexplicably replicating defaultValue values

I'm encountering an issue where, when I use JavaScript to add a new table data cell (td), it ends up copying the defaultValue and innerText of the previous td in the new cell. This is confusing to me because I am simply adding a new HTML element that ...

Exploring the Difference Between $onChanges and $onInit in Angular Components

What sets apart the use of Controller1 compared to Controller2? angular.module('app', []) .component('foo', { templateUrl: 'foo.html', bindings: { user: '<', }, controller: Controller1, ...

Javascript encounters an unforeseen < token

I encountered an unexpected token < error in my JavaScript file. Despite checking the code using JSHint, I couldn't find any issues that could resolve the problem. I attempted to place the JavaScript code in a separate file and also tried embeddin ...

Indeed, yet another problem with clearInterval

I could use some assistance as I am currently stuck trying to figure out why the stopTimer() function is not working correctly. Any guidance or suggestions would be highly appreciated. Thank you! http://jsfiddle.net/4Efbd/1/ var counter; function endTim ...

when within a 'for in' loop

Querying Object Equivalence: I find myself struggling to comprehend the flow of my code and how I can rectify it. Within the areEqual function, I am comparing the "value" property of two objects. If they match -> isEqual=true -> continue with the ...