Как сделать так, чтобы вложенный цикл продолжался только после разрешения асинхронной функции или как расширить ".then" за пределы области видимости - PullRequest
2 голосов
/ 22 апреля 2019

Я попытался предотвратить асинхронные проблемы с обещаниями в следующем коде.Используя функцию .then, все внутри этой функции вызывается после того, как функция была разрешена.Но теперь у меня возникла проблема, заключающаяся в том, что я не могу ни расширить область действия «.then», чтобы включить биты после второго цикла, ни, насколько мне известно, легко приостанавливать код до тех пор, пока функция не будет должным образом разрешена, и затем продолжитьитерация циклалибо код getZip может быть сделан синхронным, либо, если вышеупомянутый может быть выполнен.

Ответы [ 3 ]

0 голосов
/ 22 апреля 2019

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>
0 голосов
/ 22 апреля 2019

Этот вид звучит как сценарий использования для асинхронных генераторов итераторов, но, возможно, я просто слишком перегружен.У вас есть куча ресурсов, которые вы хотите перебрать, и их содержимое асинхронно.Вы хотите, чтобы он «выглядел» синхронно, поэтому вы можете использовать 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 голосов
/ 22 апреля 2019

Я не думаю, что полностью понимаю код, который вы написали.Тем не менее, я рекомендую вам использовать 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)
  });  
)};
...