【问题标题】:Synchronize multiple Promises while allowing multiple number of retries同步多个 Promise,同时允许多次重试
【发布时间】:2019-02-05 07:42:14
【问题描述】:

我正在尝试构建一个自动重试下载的下载器。基本上,一个任务队列,它重试任务一定次数。我第一次尝试使用Promise.all(),但绕过第一次拒绝失败described here 的“技巧”没有帮助(并且是该​​线程中进一步描述的反模式)

所以我得到了一个版本,它似乎在某种程度上符合我的要求。至少它打印的结果是正确的。但它仍然会抛出几个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() 会导致一些时间问题。

感谢对我的问题的任何改进或更好的解决方案。例如。我的解决方案只允许“分波”重试。如果有人提出更好的连续解决方案,那也将不胜感激。

【问题讨论】:

  • 正如您正确假设的那样,您有时间问题。在您退出内部循环并执行 tasks = failedTasks 之前,您推送到 failedTasks 的重新启动任务可能会再次失败,实际上您没有附加到该 Promise 的 .catch 回调,直到您在内部循环中再次到达它,所以很有可能您会收到未处理的拒绝错误。
  • "并且是该线程中进一步描述的反模式" - 不,根本不是。在那个答案上查看我的 cmets。
  • @Lniese 我似乎通过稍后添加.catch() 来捕获异常。这是有道理的,但也令人困惑。但我想解释器不知道我是否在稍后发现错误并且必须在某个时候抛出。我想知道是否可以选择说“我稍后会捕捉到这个异常,别担心”@Bergi 感谢您的澄清。我实际上遇到了您警告的问题。好吧,感谢接受的答案,我现在有一个更理智的代码结构。

标签: javascript async-await es6-promise


【解决方案1】:

使用awaitasnyc 可以更清晰地解决这个问题。

您将tasks 的数组(执行时启动给定任务的函数)传递给execute_tasks。该函数将为每个任务调用execute_task,将任务函数传递给它,execute_task 将返回一个 Promise,其中包含任务是否成功的信息。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-21
    • 2021-11-01
    • 1970-01-01
    • 2012-08-20
    • 2018-12-26
    • 2019-08-21
    • 2013-09-27
    • 1970-01-01
    相关资源
    最近更新 更多