mike hennessy
mike hennessy

Reputation: 1679

How to enforce promise in array loop

I'm trying to get the basics of how to implement promises and Promise.All within a given array loop, for example, from a firebase query, which can perform any number of actions within the loop, and then I have access to the results later. I can't seem to get the correct syntax, logic . below is a sample method in which I'd like to delay insert some items into an array and then have access to the array after the loop for processing.

I've researched different methods on Stackoverfllow. Some assign a promises = the snap.forEach loop and then somehow resolve this after completion. Others are creating a promise inside the loop. In my example below, I'm just using the settimeout to delay/create an async process.

        testfirebaseData = () => {

        let channels =[];
        var promises=[];
         firebase.database().ref('challengeResponses').child(day).once('value', snap => {

          snap.forEach(channelChild => {

                promises.push(new Promise((resolve) => {
                    setTimeout(() => {
                    channels.push(channelChild.key);
                    resolve(channels)
                   },1000)
                })
                )

             })             
        }).then (() => {
         Promise.all(promises).then(() => {
            console.log(channels[0])
        })

       }


    }

I would expect the output of the above to show the first element in the "channels" array, but it's always coming back with "undefined"..as I clearly am not processing the promises correctly. What am i missing?

Upvotes: 1

Views: 123

Answers (2)

EugenSunic
EugenSunic

Reputation: 13693

Here is sample example using your code style. Instead of objects within the array I've put numbers;

https://jsfiddle.net/th3vecmg/4/

 var promises = [];
 let channels =[];

 [1, 2, 3, 4, 5].forEach((elm, i) => {

   promises.push(new Promise((resolve) => {
     setTimeout(() => {
      channels.push(elm)
       resolve(elm);
     },  1000)
   }))

 });
 console.log(channels[0]) // gives undefined
 Promise.all(promises).then(() => {
   console.log(channels[0]) // gives 1
 })

You should not be getting undefined inside the then because it's waiting for the promise to execute. You will only get undefined it you are trying to display the result outside the then scope

The code doesn't make really sense since you are displaying a global variable inside the then rather then relying on the output of the promise

If you want to delay execution by 1 then do some multiplication in the timeArgument of the setTimeout...

Upvotes: 1

Patrick Evans
Patrick Evans

Reputation: 42736

Your new Promise(...) should wrap the firebase call as that is the async operation. And not be in the callback. There is no async operations going on inside the callback just plain array push which is a sync op

var promise = new Promise((resolve)=>{
   firebase.database().ref('challengeResponses').child(day).once('value', snap => {
     var channels=[];
     snap.forEach(channelChild => {
       channels.push(channelChild.key);
     });
     resolve(channels);
   });
});

promise.then((channels)=>{
    console.log(channels);
});

If you want a delay on the whole operation:

var promise = new Promise((resolve)=>{
   firebase.database().ref('challengeResponses').child(day).once('value', snap => {
     var channels=[];
     setTimeout(()=>{
       snap.forEach(channelChild => {
         channels.push(channelChild.key);
       });
       resolve(channels);
     },1000);
   });
});

To simulate nested async operations you would add the promises to an array like you were but resolve Promises.all() in the outer promise and resolve your data in the inner promises:

var promise = new Promise((resolve)=>{
   firebase.database().ref('challengeResponses').child(day).once('value', snap => {
      var snapPromises=[];
      snap.forEach(channelChild => {
         snapPromises.push(new Promise((snapresolve)=>{
           setTimeout(()=>{
             snapresolve(channelChild.key);
           },1000)
         });
      });
      resolve(Promise.all(snapPromises));         
   });
});

Upvotes: 0

Related Questions