【问题标题】:How can you retry after an exception in Javascript when using promises?使用 Promise 时如何在 Javascript 中出现异常后重试?
【发布时间】:2015-08-08 20:52:56
【问题描述】:

我正在使用 Bluebird Promise 库。我有一系列承诺功能,如下所示:

    receiveMessageAsync(params)
    .then(function(data)) {
        return [data, handleMessageAsync(request)];
    })
    .spread(function(data, response) {
        return [response, deleteMessageAsync(request)];
    })
    .spread(function(response, data) {
        return sendResponseAsync(response);
    })
    .then(function(data) {
        return waitForMessage(data);
    })
    .catch (function(err) {
       // handle error here
    });

有时 sendMessage 会失败,例如,要响应的服务器不可用。我希望代码继续尝试永远响应,直到它成功。您不能简单地将 sendMessage 包装在 catch 中,因为它实际上并没有引发异常,我想它调用了“错误”函数,在这个承诺的代码中,它是底部的“catch”。所以必须有一些方法可以在“catch”部分“重试”发送消息。问题是,即使我在“catch”中循环重试,我仍然无法跳到承诺链并执行剩余的承诺功能。我该如何处理?

编辑:

我对 HTTP 帖子的重试结果如下所示:

function retry(func) {
    return func()
        .spread(function(httpResponse) {
            if (httpResponse.statusCode != 200) {
                Log.error("HTTP post returned error status: "+httpResponse.statusCode);
                Sleep.sleep(5);
                return retry(func);
            }
        })
        .catch(function(err) {
            Log.err("Unable to send response via HTTP");
            Sleep.sleep(5);
            return retry(func);
        });
}

【问题讨论】:

  • 我在这里看不到sendMessage
  • 对不起,sendResponse 应该是 sendMessage
  • 我认为我说我想永远重试很有趣,但到目前为止,每个答案都是为有限次数的重试而编写的。一定有一些关于永远循环的事情让人们感到困扰。

标签: javascript node.js exception-handling promise bluebird


【解决方案1】:

这是一个示例重试功能(尚未测试):

function retry(maxRetries, fn) {
  return fn().catch(function(err) { 
    if (maxRetries <= 0) {
      throw err;
    }
    return retry(maxRetries - 1, fn); 
  });
}

这个想法是,您可以包装一个函数,该函数返回一个承诺,该函数将捕获并重试错误,直到重试次数用完。所以如果你要重试sendResponseAsync:

receiveMessageAsync(params)
.then(function(data)) {
    return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
    return [response, deleteMessageAsync(request)];
})
.spread(function(response, data) {
    return retry(3, function () { return sendResponseAsync(response); });
})
.then(function(data) {
    return waitForMessage(data);
})
.catch (function(err) {
   // handle error here
});

由于retry 承诺在所有重试都用尽之前实际上不会抛出,因此您的调用链可以继续。

编辑

当然,如果你愿意,你可以永远循环:

function retryForever(fn) {
  return fn().catch(function(err) { 
    return retryForever(fn); 
  });
}

【讨论】:

  • return retry(3, function () { return sendResponseAsync(response); }); 可以是return retry(3, sendResponseAsync.bind(null, response));
  • 是的,只要你不使用古老的 JS。
  • 请解释 .bind(null, response) 的作用及其工作原理。我以前使用过 bind,但仅在 .bind(this) 的意义上使用,以便我可以访问我的对象在承诺链中的范围。
  • 很好的答案,但我还有一个问题。这个函数是当前对象的所有方法,所以一开始我绑定了“this”。所以开始真的看起来像“receiveMessageAsync(params).bind(this)”,然后我称之为“this.sendMessageAsync”。当我将它包装在“重试”函数中时,我不再可以访问“this”。怎么重新绑定?
  • 使用 .bind(null, param1, param2, ...) 解决了我上面的问题,但我不知道为什么
【解决方案2】:

这是一个类似于then 的小助手,但会重试该函数。

Promise.prototype.retry = function retry(onFulfilled, onRejected, n){
    n = n || 3; // default to 3 retries
    return this.then(function(result) {
         return Promise.try(function(){ 
             return onFulfilled(result); // guard against synchronous errors too
         }).catch(function(err){
             if(n <= 0) throw err;
             return this.retry(onFulfilled, onRejected, n - 1);
         }.bind(this)); // keep `this` value
    }.bind(this), onRejected);
};

这会让你的代码写得更漂亮:

receiveMessageAsync(params)
.then(function(data)) {
    return [data, handleMessageAsync(request)];
})
.spread(function(data, response) {
    return [response, deleteMessageAsync(request)];
})
.retry(function(response, data) {
    return sendResponseAsync(response); // will retry this 3 times
})
.then(function(data) {
    return waitForMessage(data);
})
.catch (function(err) {
   // I don't like catch alls :/ Consider using `.error` instead.
});

【讨论】:

  • 请不要扩展本地人......或者如果你这样做,至少检查一下:if( !Promise.prototype.retry ){
【解决方案3】:

我刚刚发布了https://github.com/zyklus/promise-repeat,它会重试一个承诺,直到它超时或达到最大尝试次数。它允许你写:

receiveMessageAsync(params)
...
.spread(retry(
    function(response, data) {
        return sendResponseAsync(response);
    }
))
...

【讨论】:

  • 正是我想要的! +1
猜你喜欢
  • 2011-10-28
  • 2022-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 2012-09-10
相关资源
最近更新 更多