Синхронизировать несколько Обещаний, допуская несколько попыток - PullRequest
0 голосов
/ 30 августа 2018

Я пытаюсь создать загрузчик, который автоматически повторяет загрузку. В основном, очередь задач, которая повторяет задачи определенное количество раз. Сначала я попытался использовать Promise.all(), но «хитрость» для обхода отказа при первом отклонении , описанная здесь , не помогла (и является анти-паттерном, как описано далее в этом потоке)

Итак, у меня есть работающая версия, которая, кажется, делает то, что я хочу. По крайней мере, результаты, которые он печатает, верны. Но все равно выдается несколько uncaught exception test X ошибок / предупреждений, и я не знаю, что с этим делать.

Код:

asd = async () => {

  // Function simulating tasks which might fail.
  function wait(ms, data) {
    return new Promise( (resolve, reject) => setTimeout(() => {
      if (Math.random() > 0.5){
        resolve(data);
      } else {
        reject(data);
      }
    }, ms) );
  }

  let tasks = [];
  const results = [];

  // start the tasks
  for ( let i = 0; i < 20; i++) {
    const prom = wait(100 * i, 'test ' + i);
    tasks.push([i, prom]);
  }

  // collect results and handle retries.
  for ( let tries = 0; tries < 10; tries++){
    failedTasks = [];
    for ( let i = 0; i < tasks.length; i++) {

      const task_idx = tasks[i][0];

      // Wait for the task and check whether they failed or not.
      // Any pointers on how to improve the readability of the next 6 lines appreciated.
      await tasks[i][1].then(result => {
        results.push([task_idx, result])
      }).catch(err => {
        const prom = wait(100 * task_idx, 'test ' + task_idx);
        failedTasks.push([task_idx, prom])
      });
    }

    // Retry the tasks which failed.
    if (failedTasks.length === 0){
      break;
    } else {
      tasks = failedTasks;
    }
    console.log('try ', tries);
  }

  console.log(results);
}

В конце концов, массив results содержит (если не выполнить задачу 10 раз) все результаты. Но все же uncaught exceptions летают вокруг.

Поскольку не все отклоненные обещания приводят к неперехваченным исключениям, я подозреваю, что запуск задач сначала, а применение then()/catch() позже вызывает некоторые проблемы с синхронизацией.

Любые улучшения или лучшие решения моих проблем приветствуются. Например. мое решение позволяет повторять попытки только "волнами". Если кто-нибудь придумает лучшее непрерывное решение, это также будет высоко оценено.

1 Ответ

0 голосов
/ 30 августа 2018

Использование await и asnyc позволяет решить эту проблему гораздо яснее.

Вы передаете массив tasks (функции, которые при выполнении запускают заданную задачу) в execute_tasks. Эта функция будет вызывать для каждой из этих задач execute_task, передавая ей функцию задачи, execute_task возвращает Обещание, содержащее информацию, если задача была успешной или нет.

execute_task в виде цикла, который повторяется до тех пор, пока асинхронная задача не будет выполнена успешно или пока не будет достигнуто максимальное количество попыток.

Поскольку каждая из задач имеет свой цикл повторных попыток, вы можете избежать этих волн. Каждая задача ставится в очередь для нового выполнения, если она не выполняется. Использование await таким образом создает некоторую совместную многозадачность. И все ошибки обрабатываются, потому что задача выполняется в блоке try catch.

function wait(ms, data) {
  return new Promise((resolve, reject) => setTimeout(() => {
    if (Math.random() > 0.5) {
      resolve(data);
    } else {
      reject(new Error());
    }
  }, ms));
}


async function execute_task(task) {
  let result, lastError;
  let i = 0
  
  //loop until result was found or the retry count is larger then 10
  while (!result && i < 10) {
    try {
      result = await task()
    } catch (err) {
      lastError = err
      // maybe sleep/wait before retry
    }
    i++
  }

  if (result) {
    return { success: true, data: result }
  } else {
    return { success: false, err: lastError }
  }
}

async function execute_tasks(taskList) {
  var taskPromises = taskList.map(task => execute_task(task))
 
  // the result could be sorted into failed and not failed task before returning
  return await Promise.all(taskPromises)
}


var taskList = []
for (let i = 0; i < 10; i++) {
  taskList.push(() => {
    return wait(500, {
      foo: i
    })
  })
}

execute_tasks(taskList)
  .then(result => {
    console.dir(result)
  })
...