Search code examples
javascriptloopsasynchronousjszip

How do I make a nested loop continue only after a asynchronous function has been resolved or how do I extend ".then" beyond the scope


I tried to prevent async problems with promises in the following code. By using a .then function everything within that function gets called after the function has been resolved. But now I have the problem that neither can I extend the scope of the ".then function" enough to include the bits after the second loop nor can I to my knowledge easily pause the code until the function has been properly resolved and THEN continue with the loop iteration.

Here's my main code(simplified):

let total = []
$.each(element, function(data) {
  //Some other code
  let out;
  $.each(element2, function(data2) {
    getZip(data2).then(function(txt){ //after everything has finished this get's called
      out = someFunction(txt,data2);
      total.push(out);
    });

  )};
   console.log(total)//this gets called first 
  //some other code that does some stuff with total
)};

Here's the getZip code which is asynchronous:

        function getZip(zipFile) {
            return new Promise(function (resolve, reject){
                zip = new JSZip()
                JSZipUtils.getBinaryContent("someURL/" + zipFile, function (err, data) {
                    if (err) {
                        reject(err)
                    }
                    JSZip.loadAsync(data).then(function (zip) {
                        return zip.file(zipFile.replace(".zip", "")).async("text"); //gets the file within the zip andoutputs as text
                    }).then(function (txt) {
                        resolve(txt)
                    });

                });
            });
        }

I'd be happy if either the getZip code could be made synchronous or if the before mentioned could be done.


Solution

  • I do not think I fully understand the code you have written. However, I recommend you use Promise.all. Here is an example I have written that I hope helps guide you:

    let total = [];
    $.each([1,2,3,4], function (data) {
      // Some other code.
      let out;
      // Create a new promise so that we can wait on the getZip method.
      new Promise(function (resolve, reject) {  
        // Create a holder variable. This variable with hold all the promises that are output from the getZip method you have.
        let gZipPromises = [];
        $.each([5,6,7,8], function (data2) {    
          // Your getZip method would go here. wrap the call to getZip in gZipPromises.push to push all the returned promises onto the holding variable.
          gZipPromises.push(new Promise(function (resolve2, reject2) { 
            // Sample Code
            setTimeout(function () {
             total.push(data2); 
              resolve2(""); 
            }, 10);
            // End Sample Code.
          }));
        });  
        // Pass the holding variable to Promise.all so that all promises in the holding variable are executed before resolving.
        Promise.all(gZipPromises).then(function() { 
          resolve() 
        });
      }).then(function () {
        // This will be called only when all getZip promises are completed in the second loop.
        console.log(total);
      });
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    With that said, I could not test your code. But I think this would work: (Please note that based on the code you provided, the variable total would be logged for each iteration of the top most $.each

    let total = []
    $.each(element, function(data) {
      //Some other code
      let out;  
      // Define a new promise.
      new Promise(function (resolve, reject) {
        let gZipPromises = [];
        $.each(element2, function(data2) {
          gZipPromises.push(
            getZip(data2).then(function(txt){ //after everything has finished this get's called
              out = someFunction(txt,data2);
              total.push(out);
            });
          );
        )};
        Promise.all(gZipPromises).then(function() { 
          resolve() 
        });
      }).then(function () { 
        console.log(total)
      });  
    )};