0

As the questions says I'm trying to wait for a map to finish executing before I run another command. In this case console log. I have

let concernsInDB = [];
let ctr = 0;

    this.state.templateOptions.map(el => {
      el.details.map(elDetail => {
        this.getConcernsDB(elDetail.values)
        .then(elDetailResults => {
          let detailsToPush = {};
          if(elDetailResults.length) {
            detailsToPush[elDetailResults[0].value_id] = elDetailResults;
            concernsInDB.push(detailsToPush);
          }
        })
      })
    })
    console.log(concernsInDB);

The above code executes and I get a console.log of Array []. The console.log is resolving before all other methods are finished.

this.state.templateOptions contains an array of several objects (about 50, so I'm not putting all here):

 Object {
    "active": 1,
    "id": 1378,
    "name": "Wires are running through trees",
    "narrative": "Service drop wires are running through tree(s).It is recommended to have a contacting utility company or a qualified electrician and/or tree s",vice company to correct conditions as necessary.
    "value_id": 13935,
  },

As I loop through them I run this method: this.getConcernsDB(elDetail.values) elDetail.values is the "value_id" from the above example. I then wait for it to resolve .then push the results into concernsInDB. This has to be done on several records. How would I wait for it all to finish before console.logging concernsInDB so it'll have all the completed data?

If I wrap it in a promise the resolve would run on each loop so I don't see how I could accomplish it. Any solutions?

FabricioG
  • 3,107
  • 6
  • 35
  • 74
  • 1
    why are you using `.map` if you never return anything in the callbacks? you may as well use `.forEach` ... however, to do what you want, you actually WANT to use `.map` - but then don't IGNORE the returned value – Jaromanda X Jan 07 '20 at 02:12
  • Look into using `async` and `await` keywords. For example, if your `.forEach` (per Jaromanda X's suggestion) is inside an asyncronous function, you would be able to do something as simple as `let detailResults = await this.getConcernsDB(...)`. – matthew-e-brown Jan 07 '20 at 02:14
  • @matthew-e-brown If there is no requirement for the order of execution, `Promise.all` is the superior choice. – Amadan Jan 07 '20 at 02:15
  • There are many articles and questions on async operations and map e.g. https://stackoverflow.com/questions/40140149/use-async-await-with-array-map – Dominic Jan 07 '20 at 02:16
  • @Amandan Promise.all only works with an array/iterable of promises are passed -- I was trying to build off of Jaromanda's `.forEach` comment. If OP sticks with `map`, then yes, Promise.all is definitely the go-to! – matthew-e-brown Jan 07 '20 at 02:17

1 Answers1

1

You want to use Promise.all (twice) to wait for all the Promise based asynchrony to finish

You also want to return something in .map otherwise you'd use .forEach (.map is the correct method in this case though)

let concernsInDB = [];
let ctr = 0;

Promise.all(this.state.templateOptions.map(el => 
    Promise.all(el.details.map(elDetail => 
        this.getConcernsDB(elDetail.values)
        .then(elDetailResults => {
            let detailsToPush = {};
            if (elDetailResults.length) {
                detailsToPush[elDetailResults[0].value_id] = elDetailResults;
                concernsInDB.push(detailsToPush);
            }
        })
    ))
)).then(() => {
    console.log(concernsInDB);
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87