Await for loop JS

Viewed 39

I have a for loop in JS, and when I run it, it looks messy. In fact, I have a variable called "count" that is supposed to count the total loop index.

But in my code, I print it twice and these two prints have different values. I guess it's because the PDF download is an asynchronous function and therefore it doesn't run in the right order.

I would like to have two times the same value of "count" in two different places.

let count = 0;

for (let docNumber = 0; docNumber < values.length; docNumber++) {

    for await (const mat of values[docNumber]) {

        try {
            console.log(count); // Here is a value
            downloadPDF(mat.dates, "data/pdf/", count, spe, function(err, hasDownloaded) {
                if (err) {
                    console.log(err);
                } else {
                    if (hasDownloaded) {
                        console.log(count); // Here a different value 
                        programs[count] = mat.listProgram;
                        matieres[count] = mat.matiere;

                    }
                }
            });
            count++;
        } catch (err) {
            callback(err, undefined);
            console.log(err);
        }
    }

}
2 Answers

As you said the downloadPDF is an async function, but with callback syntax. which makes the await keyword useless in this context, And because it's an async function it will log the count after the loop finishes.

So, I suggest you promisify the downloadPDF function.

for await (const mat of values[docNumber]) {
    try {
        console.log(count); // Here is a value
        await new Promise((resolve, reject) => {
            downloadPDF(
                mat.dates,
                "data/pdf/",
                count,
                spe,
                function (err, hasDownloaded) {
                    if (err) {
                        reject(err);
                    } else if (hasDownloaded) {
                            console.log(count); // Here a different value
                            programs[count] = mat.listProgram;
                            matieres[count] = mat.matiere;
                            resolve();
                    } else {
                      reject()
                    }
                }
            );
        });
        count++;
    } catch (err) {
        callback(err, undefined);
        console.log(err);
    }
}

Here is the single/double digit for doc_size recursive variant I prefer when trying to loop an async function. I feel like the inside call needs to use docNumber to work though not part of the question. Here is the format.

let count = 0;
let doc_size = values.length
const async_helper = async (docNumber=0) => {
    if(docNumber===doc_size) return;
    const mat = values[docNumber] // edit added this line!
    console.log(count); // Here is a value
    await downloadPDF(mat.dates, "data/pdf/", count, spe, function(err, hasDownloaded) {
        if (err) {
            console.log(err);
        } else {
            if (hasDownloaded) {
                console.log(count); // Here a different value 
                programs[count] = mat.listProgram;
                matieres[count] = mat.matiere;

            }
        }
    });
    count++;
    await async_helper(docNumber+1)
}
async_helper().then((_)=>{
    console.log(programs);
}).catch (err) {
    console.log(err); // <- move this above callback
    // otherwise it's possible it never logs out
    callback(err, undefined);
}

I think count should behave now, though it will be the same count value, it will have proper value in order.

Edit here is the non-recursive variant I suggest for a heftier procedure.

const download_all = async (values) => {
    const inner_promises = [];
    let doc_size = values.length
    if (doc_size) {
        for (let i = 0; i < doc_size; i++) {
            const mat = values[docNumber] // edit added this line!
            inner_promises.push(
                new Promise((res, rej) => {
                    try {
                    downloadPDF(mat.dates, "data/pdf/", count, spe, function(err, hasDownloaded) {
                        if (err) {
                            console.log(err);
                        } else {
                            try{
                                if (hasDownloaded) {
                                    console.log(count); // Here a different value
                                    res({ 
                                        programs: mat.listProgram,
                                        matieres: mat.matiere
                                    })
                                }
                            } catch (err) {
                                rej(err)
                            }
                        }
                    });
                    }
                }),
            );
        }
    }
    return Promise.all(inner_promises);
};
download_all(values).then((res)=>{
    console.log(res);
}).catch (err) {
    console.log(err);
    callback(err, undefined);
}

I think removing the inner catch is appropriate if you just reject there anyway.
Edit these were pulled from my new nodedemon, if you wanna see it in Typescript context it's here:
github proj line link I recommend the first recursive variant unless You want everything at once.

Related