如何使嵌套循环仅在解决异步函数后继续,或者如何将“.then”扩展到范围之外

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

我试图在以下代码中防止 promise 出现异步问题。通过使用 .then 函数,该函数中的所有内容都会在函数解析后被调用。但是现在我遇到了一个问题,即我既不能扩展“.then 函数”的范围以包含第二个循环之后的位,也不能据我所知轻易地暂停代码直到函数被正确解析然后继续循环迭代。

这是我的主要代码(已简化):

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
)};

这是异步的 getZip 代码:

        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)
                    });

                });
            });
        }

如果 getZip 代码可以同步或者前面提到的可以完成,我会很高兴。

我不认为我完全理解你写的代码。但是,我建议您使用 Promise.all。这是我写的一个例子,希望能对你有所帮助:

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>

尽管如此,我无法测试您的代码。但我认为这会起作用: (请注意,根据您提供的代码,变量 total 将针对最顶层 $.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)
  });  
)};

const elements = [["foo.zip"],["bar.zip"],["baz.zip"]];
const totalOut = getAllZips(elements)
  .then(text => console.info(text))
  .catch(error => console.error(error))

function someFunction(text, data) {
  return `${text}\nLength: ${data.length}`;
}

async function getAllZips(elements) {
  let promises = [];
  for(const element of elements) {
    for(const data of element) {
      promises.push(getZip(data).then(text => {
        return someFunction(text, data);
      }));
    }
  }
  return Promise.all(promises);
}

async function getZip(file) {
  return new Promise((resolve, reject) => {
    JSZipUtils.getBinaryContent(`someURL/${file}`, async (err, data) => {
      try {
        if (err) throw err;
        const zip = await JSZip.loadAsync(data);
        const name = file.replace(".zip", "");
        resolve(await zip.file(name).async('text'));
      } catch(error) {
        reject(error);
      }
    });
  });
}
<script>/*IGNORE*/const JSZipUtils = {getBinaryContent:(p,c)=>errs.gbc?c(new Error('gbc'),null):c(null,{foo:true})};const JSZip = {loadAsync:(d)=>errs.la?Promise.reject(new Error('la')):({file:n=>({async:a=>errs.a?Promise.reject(new Error('a')):Promise.resolve('Hello World')})})};const errs = {gbc:false,la:false,a:false};/*IGNORE*/</script>

这种听起来像是异步迭代器生成器的用例,但也许我只是过度设计了。您有一堆要迭代的资源,它们的内容是异步的。您希望它 "look" 同步,因此您可以利用 async/await:

function getZip(zipFile) {
  /*
   * Theres no point in simplifying this function since it looks like
   * the JSZip API deals with callbacks and not Promises.
   */
  return Promise.resolve(zipFile);
}

function someFn(a, b) {
  return `${a}: ${b.length}`;
}

async function* zipper(elements) {
  for (const element of elements) {
    for (const data of element) {
      const txt = await getZip(data);
      yield someFn(txt, data);
    }
  }
}

(async() => {
  const elements = [
    ["hello"],
    ["world"],
    ["foo"],
    ["bar"]
  ];
  let total = [];
  for await (const out of zipper(elements)) {
    total.push(out);
  }
  console.log(total);
})();