Home > OS >  Iterate over array with promise inside and wait to finish
Iterate over array with promise inside and wait to finish

Time:10-08

I'm trying populate a response array with Firestore snapshot and inside each snapshot, create a download link of stored files. I tried solutions with Promises but always the response array was null.

docRef.get().then(async (snapshot: any) => {
                await snapshot.docs.forEach(async (attachment: any) => {                    
                    await downloadFile(attachment.data()["paths"]).then((urls: any) => {
                        attachmentList.push({
                            "id": attachment.id,
                            "created_at": attachment.data()["created_at"],
                            "paths": urls,
                            "content_types": attachment.data()["content_types"]
                        })
                    }).catch(error => {
                        res.status(400).send({
                            "code": "ERROR",
                            "message": error
                        });
                    })  
                })
            })
            res.send({
                "code": "ok",
                "message": attachmentList
            });

CodePudding user response:

This is a classic case for Promise.all. try this way

const snapshot = await docRef.get();
const results = await Promise.all(
  snapshot.docs.map(
    attachment => downloadFile(attachment.data()["paths"])
  )
);
console.log(results);

CodePudding user response:

If we clean your indentation up, you can quickly see why you aren't getting back anything for "message" regardless of what you were trying in there - it returns the response before any of the asynchronous stuff even executes:

docRef.get()
    .then(async (snapshot: any) => {
        // <-- this line won't run
        /* ... removed for conciseness */
    });

res.send({
    "code": "ok",
    "message": attachmentList
});
// <-- before this line (and the response is sent already!)

What you are looking to do is to assemble your array of attachments, and then return them:

docRef.get()
    .then((snapshot) => {
        return Promise.all(
            snapshot.docs.map(async (attachmentDocSnapshot) => {
                const { paths, created_at, content_types } = attachmentDocSnapshot.data();
                const urls = await downloadFile(paths);
                return {
                    content_types,
                    created_at,
                    id: attachmentDocSnapshot.id,
                    paths: urls
                };
            })
        );
    })
    .then((attachmentInfoList) => {
        res.json({                                  // .json() is more semantic
            "code": "OK",                           // for consistency, capitalized
            "message": attachmentInfoList
        });
    })
    .catch((err) => {
        console.error(`Failed to collect information about the attachments for Doc #${docRef.id}: `, error);
        res.status(500).json({                      // 500 Internal Server Error
            "code": "ERROR",
            "message": error.code || error.message  // Send back as little information about the error as possible
        });
    });

While the above code works, it's not pretty. So let's rework it into a child function:

async function getAttachmentInfoFromSnapshot(attachmentDocSnapshot) {
    const { paths, created_at, content_types } = attachmentDocSnapshot.data();
    const urls = await downloadFile(paths);
    return {
        content_types,
        created_at,
        id: attachmentDocSnapshot.id,
        paths: urls
    };
}

docRef.get()
    .then((snapshot) => Promise.all(
        snapshot.docs.map(getAttachmentInfoFromSnapshot)
    ))
    .then((attachmentInfoList) => {
        res.json({                                  // .json() is more semantic
            "code": "OK",                           // for consistency, capitalized
            "message": attachmentInfoList
        });
    })
    .catch((err) => {
        console.error(`Failed to collect information about the attachments for Doc #${docRef.id}: `, error);
        res.status(500).json({                      // 500 Internal Server Error
            "code": "ERROR",
            "message": error.code || error.message  // Send back as little information about the error as possible
        });
    });

Note: You could rewrite the above to use async/await syntax on the next level up, but if you do, make sure to wrap it all in a try/catch block to handle any errors along the way.

  • Related