Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for asynchronous JSZip .forEach() call to finish before running next code?

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?

like image 484
Pranav Avatar asked Jan 20 '19 08:01

Pranav


People also ask

How do you wait for async forEach to finish?

To wait for . forEach() to complete with JavaScript, we should replace forEach with a for-of loop. const myAsyncLoopFunction = async (array) => { const allAsyncResults = []; for (const item of array) { const asyncResult = await asyncFunction(item); allAsyncResults. push(asyncResult); } return allAsyncResults; };

How do I make async call wait?

An alternative way to wait for all promises to be done, you have to use async and await keywords, just add async keyword before function checkPhotos(... and ads await keyword before Promise. all... , that's will tell js engine that you need to run the code below await line when you got the response.

Can we use await in forEach JavaScript?

Using Babel will transform async / await to generator function and using forEach means that each iteration has an individual generator function, which has nothing to do with the others. so they will be executed independently and has no context of next() with others.

How use async await JS?

async and await Inside an async function, you can use the await keyword before a call to a function that returns a promise. This makes the code wait at that point until the promise is settled, at which point the fulfilled value of the promise is treated as a return value, or the rejected value is thrown.


2 Answers

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
});
like image 160
Patrick Roberts Avatar answered Oct 02 '22 09:10

Patrick Roberts


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.

like image 44
Vahid Avatar answered Oct 02 '22 08:10

Vahid