使用promises时如何在Javascript中的异常后重试?

时间:2015-05-27 00:17:12

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

我正在使用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中,因为它实际上并没有抛出异常,我想,它调用了“error”函数,在这个promisified代码中,它是底部的“catch”。因此必须有一些方法可以在“catch”部分“重试”发送消息。问题是,即使我在“catch”中循环重试,我仍然没有办法跳到promise链并执行剩余的promisified函数。我该如何处理?

编辑:

我对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);
        });
}

3 个答案:

答案 0 :(得分:7)

这是一个样本重试功能(尚未测试):

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

这个想法是你可以包装一个返回一个promise的函数,该函数将捕获并重试错误,直到重试失败。因此,如果您要重试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); 
  });
}

答案 1 :(得分: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.
});

答案 2 :(得分:0)

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

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