【问题标题】:Handling exceptions within recursive promise在递归承诺中处理异常
【发布时间】:2017-07-29 18:37:05
【问题描述】:

我正在尝试既能够处理分页 API,又能够在因过多请求而受到限制时重试。如果响应对象中存在“nextToken”,则通过递归处理分页。我希望能够捕获一个限制异常,并通过在不传递令牌的情况下递归有效地重新启动整个请求。这是我当前的代码:

function getAllExecHist(execArn) {
  var sfn = new AWS.StepFunctions();
  sfn = Promise.promisifyAll(sfn); 
  execHists = [];
  return new Promise(function(resolve, reject) {
    function getExecHist(nextToken) {
      params = {};
      params.executionArn = execArn;
      if (nextToken !== undefined) {
        params.nextToken = nextToken;
      }
      sfn.getExecutionHistoryAsync(params)
        .then(function(results) {
          execHists = execHists.concat(results.events);
          if (!results.nextToken) {
            resolve(execHists);
          }
          else {
            getExecHist(results.nextToken);
          }
        })
        .catch(function(e) {
          console.log('caught this: ', e);
          console.log('retrying');
          return new Promise(function(res, rej) {
          console.log('Sleeping');
          setTimeout(function() {
            execHists = [];
            res(getExecHist()); 
          }, random(100,10000));
          });         
        })
    }
    getExecHist();
  });
}

递归处理分页没有问题,但自从添加了catch,它就永远不会返回。任何想法我做错了什么/如何解决?

【问题讨论】:

  • getExistHist() 不返回任何内容,因此不清楚您要对 res(getExecHist()) 做什么。
  • 您的execHistsparamsaccidentally global
  • 只是好奇,为什么要在出错后重新开始,现在的结果不应该仍然有效吗?
  • 您是否遇到任何错误?你的日志是什么样子的?

标签: node.js recursion promise


【解决方案1】:

AWS SDK supports promises,您可以将 Bluebird 配置为它的 Promise 库。

const Promise = require('bluebird');
const AWS = require('aws');
AWS.config.setPromisesDependency(Promise);
const sfn = new AWS.StepFunctions();

使用Promise.delay() 代替setTimeout

如果函数已经返回它们,请尽量避免创建新的 Promise。仅当您有很多可能引发错误或需要尽早解决承诺的同步代码时,才将承诺包装在 new Promise 中。

以下还通过在函数调用之间传递值来避免额外的函数和嵌套范围。

function getExecHist(execArn, execHists, nextToken) {
  let params = {};
  params.executionArn = execArn;
  if ( nextToken !== undefined ) params.nextToken = nextToken;
  if ( execHists === undefined ) execHists = [];
  return sfn.getExecutionHistory(params).promise()
    .then(results => {
      execHists = execHists.concat(results.events);
      if (!results.nextToken) return execHists;
      return getExecHist(execArn, execHists, results.nextToken);
    })
    .catch(e => {
      console.log('caught this: ', e);
      console.log('retrying');
      return Promise.delay(random(100,10000))
        .then(() => getExecHist(execArn));
    })
}

最终,您应该具体说明重试的错误,并包括计数或时间限制。

另请注意,这是重试速率限制问题的错误方法,因为它会从头开始。速率限制重试应该从它停止的地方继续,否则你只是增加了速率限制问题。

【讨论】:

  • 您应该使用.then(…, …) instead of .then(…).catch(…) 来捕捉正确的错误,并避免在递归中无限链接承诺
  • 谢谢。这段代码看起来很漂亮。点在你所有的 cmets 上。我很可能再也不会使用这段代码了,所以目前它确实很草率。不过,我希望您能澄清一下——我在原始示例中所做的某些具体操作会阻止函数返回吗?
  • @Bergi “避免无限链接承诺”fn().catch()fn.then(s,e) 处理承诺链的方式是否存在功能差异?
  • 是的,存在功能差异,请参阅我的第一条评论中的链接。关于无限链,内存消耗见these considerations
  • @Bergi 我得到了 try/catch 语义,但不清楚 then(s, e) 如何避免递归中的链?到目前为止,我所能看到的只是 .catch 每个都可以在 oom 之前容纳大约 1000 万个承诺:/
猜你喜欢
  • 2017-03-26
  • 2019-06-23
  • 2016-04-02
  • 1970-01-01
  • 2021-10-07
  • 2016-12-05
  • 1970-01-01
  • 2014-02-04
  • 1970-01-01
相关资源
最近更新 更多