2
votes

Comment faire en sorte qu'une boucle imbriquée continue seulement après qu'une fonction asynchrone a été résolue ou comment étendre ".then" au-delà de la portée

J'ai essayé d'éviter les problèmes asynchrones avec les promesses dans le code suivant. En utilisant une fonction .then, tout ce qui se trouve dans cette fonction est appelé une fois que la fonction a été résolue. Mais maintenant, j'ai le problème que je ne peux pas non plus étendre suffisamment la portée de la "fonction .then" pour inclure les bits après la deuxième boucle ni, à ma connaissance, mettre facilement le code en pause jusqu'à ce que la fonction ait été correctement résolue et PUIS continuer avec l'itération de la boucle.

Voici mon code principal (simplifié):

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

                });
            });
        }

Voici le code getZip qui est asynchrone:

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

Je serais heureux si le code getZip pouvait être rendu synchrone ou si ce qui précède pouvait être fait.


1 commentaires

pourrait envelopper la boucle dans une fonction asynchrone anonyme et attendre la promesse, ou dans votre boucle, pousser les promesses vers un tableau puis appeler promise.all ()


3 Réponses :


0
votes

Je ne pense pas comprendre parfaitement le code que vous avez écrit. Cependant, je vous recommande d'utiliser Promise.all . Voici un exemple que j'ai écrit et qui, je l'espère, vous aidera à vous guider:

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)
  });  
)};
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Cela dit, je n'ai pas pu tester votre code. Mais je pense que cela fonctionnerait: (Veuillez noter qu'en fonction du code que vous avez fourni, la variable total serait enregistrée pour chaque itération du plus haut $.each

p>

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


0 commentaires

0
votes

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


0 commentaires

0
votes

Cela ressemble à un cas d'utilisation pour les générateurs d'itérateurs asynchrones, mais peut-être que je ne fais que sur-ingénierie. Vous avez un tas de ressources sur lesquelles vous souhaitez parcourir et leur contenu est asynchrone. Vous voulez qu'il "paraisse" synchrone, vous pouvez donc tirer parti de 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);
})();


0 commentaires