Bluebird Javascript Promise:处理指定错误后停止执行

时间:2015-06-12 10:52:55

标签: javascript node.js promise bluebird

我有一个有点复杂的流程,从数据库接收entryUrl,检查重定向到的位置,然后用exitUrl更新它。

基本上流程应该是这样的:

  • 在没有退出网址的情况下检索Url
    • 使用Url.entryUrl获取request标头
      • 如果出现意外回复或重置连接,请标记此Url并继续下一个
    • 解析exitUrl执行的request
      • 存储exitUrl
      • 继续下一个Url
  • 如果没有Url可用,请在5秒后重试
  • 如果上述链或子链中出现任何意外错误,请在60秒后重试

我当前的实现是这样的,使用Bluebird javascript promise样式:

function processNext() {
    return api.getUrlWithoutExitUrl()
    .then(function followEntryUrl(url)
    {
        if (!url || !url.entryUrl)
        {
            throw new NoUrlAvailableError();
        }

        log.info('getting exit url for ' + url.entryUrl);
        return [
            request({
                method              : 'HEAD',
                url                 : url.entryUrl,
                followAllRedirects  : true,
                maxRedirects        : 20
            })
            .catch(ResponseError, function()
            {
                log.error('got strange response');
            })
            .catch(ConnResetError, function()
            {
                log.error('connection was reset');
            })
            .then(function removeInvalidUrl()
            {
                log.info('remove invalid url'); //FIXME: after doing this, we should not continue with the other `then` calls
            }),
            url
        ];
    })
    .spread(function parseExitUrl(res, url)
    {
        if (!res[0] || !res[0].request || !res[0].request.uri || !res[0].request.uri.href)
        {
            throw new InvalidUrlError();
        }
        return [res[0].request.uri, url];
    })
    .spread(function storeExitUrl(parsedExitUrl, url)
    {
        return api.setUrlExitUrl(url, parsedExitUrl);
    })
    .then(processNext)
    .catch(InvalidUrlError, function()
    {
        log.info('an attempted url is invalid, should set as processed and continue with next immediately');
    })
    .then(processNext)
    .catch(NoUrlAvailableError, function()
    {
        log.info('no url available, try again after a while');
    })
    .delay(5000)
    .then(processNext)
    .catch(function(err)
    {
        log.error('unexpected error, try again after a long while');
        log.error(err);
        log.error(err.constructor);
    })
    .delay(60000)
    .then(processNext);
}
processNext();

function ResponseError(e)
{
    return e && e.code === 'HPE_INVALID_CONSTANT';
}

function ConnResetError(e)
{
    return e && e.errno === 'ECONNRESET';
}

现在问题是,如果有ConnResetErrorResponseError,则会按原样执行catch块,但then块会跟随spread 1}}调用也被执行 - 但我希望执行在捕获这两种特定错误类型之后完成某些操作后停止。

我如何实现这样的执行流程?

2 个答案:

答案 0 :(得分:0)

就像在同步代码中一样 - 如果你有catch要执行某些处理然后传播错误 - 你可以重新抛出它:

同步代码:

try {
    a = makeRequest();
} catch(e) { 
    // handle
    throw e;
}

承诺:

makeRequest().catch(e => {
    // handle
    throw e; // also a good idea to add data to the error here
});

答案 1 :(得分:0)

从您的内心承诺,当您第一次catch ResponseError或ConnResetError时,您正常返回(即不投掷),因此后续的承诺链成功,执行其then()spread()分支,而不是失败并前往catch()分支。

您可能想要重写内部承诺catch块,如下所示:

...
.catch(ResponseError, function(err) {
     log.error('got strange response');
     throw err;
})
...

基本上,如果您想继续将其视为错误,请重新抛出您抓到的Error