8

I have a global variable called 'data' that is being modified inside a forEach loop. However, since the loop is asynchronous, the code does not wait until data is populated before it continues with the code. This is using the JSZip library.

let data = [];

await zip.folder("folderName").forEach(async function (relativePath, file) {
            let json = await zip.file(file.name).async("text");
            data.push(json);
            console.log(data.length); // prints increasing numbers
        });

console.log(data.length); //prints 0
// need to do something with data but it is empty

How do I wait for the data array to be populated before I continue with the code?

Pranav
  • 149
  • 2
  • 6
  • Looking at the api, `forEach` does not look to be async, so sending a promise to it wont work, its expecting a callback.. – Keith Jan 20 '19 at 08:35
  • 1
    Possible duplicate of [How can I wait for set of asynchronous callback functions?](https://stackoverflow.com/questions/10004112/how-can-i-wait-for-set-of-asynchronous-callback-functions) and many, many others – JJJ Jan 20 '19 at 08:38
  • @JJJ not really. Most of those answers and the "many, many others" will suggest using `map()` which is not an option here, or manually iterating the set with a `for` loop, which is also not possible here without making assumptions about the internals of the API. – Patrick Roberts Jan 20 '19 at 18:22
  • I cannot see how map could not be used here. – JJJ Jan 20 '19 at 21:13
  • @JJJ might want to look at the API before blinding closing as a duplicate then. The return value is not an array, it's an API-specific object. – Patrick Roberts Jan 21 '19 at 01:10

2 Answers2

9

forEach() has no return value so it cannot be awaited. You'll have to populate an array of promises from each ZipObject#async() and await that array using Promise.all() to get the results:

const promises = [];

zip.folder("folderName").forEach(function (relativePath, file) {
  promises.push(zip.file(file.name).async("text"));
});

Promise.all(promises).then(function (data) {
  // do something with data
});
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
4

According to JSZip documentation, there's no way to convert forEach(callback) to an array of Promises. So the only way I came up with was to get the number of files and use a counter.

const myFolder = zip.folder("folderName");
const numberOfCallbacks = Object.keys(myFolder.files).length - 1;
let counter = 0;
myFolder.forEach((relativePath, file) => {
    // your code. you'd better create a Promise here and add it to an array of promises.
    counter++;
    if (counter === numberOfCallbacks) {
        // everything is done. If you created Promise above, here you can use Promise.all()
    }
});

I tested the above code and it worked. Let me know if there's a problem.

Vahid
  • 6,639
  • 5
  • 37
  • 61