hirefrank
hirefrank

Reputation: 23

Javascript function finishing out of order -- async

I have a simple function that calls a firestore collection and returns a list of companies. I iterate through the list and output the company name. Once it's complete, I want to write "done".

function getCompanies() {
    firestore.collection('companies')
        .listDocuments()
        .then(companies => {
            for(var i in companies) {
                companies[i].get().then(company => {
                    console.log(company.name);
                });
            }
        }).catch(err => {
        console.error(err);
    });
};

getCompanies();
console.log('done');

In reality, this is what happens...

done
ford
gm
toyota
jeep
vw

I've looked into promises...

function getCompanies() {
    firestore.collection('companies')
        .listDocuments()
        .then(companies => {
            let promises = [];

            for(var i in companies) {
                companies[i].get().then(company => {
                    promises.push(doIt(company.id));
                });
            }

            Promise.all(promises)
                .then((results) => {
                console.log("All done", results);
                })
                .catch((e) => {
                    // Handle errors here
                });

        }).catch(err => {
        console.error(err);
    });
};

function doIt(value) {
    return new Promise((resolve) => {
    setTimeout(() => {
        console.log(value);
        resolve(value);
    }, Math.floor(Math.random() * 1000));
    });
}

getCompanies();

But that didn't work either...

All done []
ford
gm
toyota
jeep
vw

Any pointers or advice? I feel like I'm forgetting to do something obvious :/.

Thanks in advance!

Upvotes: 0

Views: 72

Answers (3)

ram
ram

Reputation: 690

This is a great overview of promises which explains which blocks of code are asynchronous.

https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f

If you want 'done' to print in the correct order it must be called within the async block.

If I understand correctly, .get() returns a promise so you would need to do something like this.

function getCompanies() {
  firestore
    .collection('companies')
    .listDocuments()
    .then(companies => {
      const myPromisesArr = companies.map(i => i.get());

      Promises.all(myPromisesArr).then(companies => {
        companies.forEach(company => {
          console.log(company.id);
        });
      });
    })
    .catch(error => console.error(error))

    .finally(() => console.log(`done`));
}

Upvotes: 0

Aritra Chakraborty
Aritra Chakraborty

Reputation: 12552

That is because the console.log is synchrnous and getCompanies() is asynchrnous. The getCompanies will resolve in the future. Read up on Promise and async/await a bit.

A quick fix without using async/await will be:

function getCompanies() {
    return firestore.collection('companies')
        .listDocuments()
        .then(companies => {
            const promises = [];
            for(var i in companies) {
                promises.push(companies[i].get());
            }
            return Promise.all(promises);
        }).catch(err => {
        console.error(err);
    });
};

Now to run the getCompanies and console.log in order, you need to execute the console.log after getCompanies resolve

getCompanies()
.then((companiesarr)=>{
    companiesarr.forEach((c)=>{
        console.log(c.name)
    })
}).then(()=>console.log('done'))

Upvotes: 4

Ashish Modi
Ashish Modi

Reputation: 7770

You need to return the proimse from your function like this

function getCompanies() {
    return firestore.collection('companies')
        .listDocuments()
        .then(companies => {
            for(var i in companies) {
                companies[i].get().then(company => {
                    console.log(company.name);
                });
            }
        }).catch(err => {
        console.error(err);
    });
};

Upvotes: 0

Related Questions