2015-05-27 1 views
6

Я использую библиотеку обещаний Bluebird. У меня есть цепь promisified функций вроде следующего:Как вы можете повторить попытку после исключения в Javascript при использовании обещаний?

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», которая в этом многообещающем коде является «catch» внизу. Поэтому в разделе «catch» должен быть какой-то способ «повторить» отправку сообщения. Проблема в том, что даже если я повторюсь в цикле в «catch», у меня все еще нет возможности перейти к цепочке обещаний и выполнить оставшиеся обещанные функции. Как мне с этим справиться?

EDIT:

Моя повторная попытка для 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); 
     }); 
} 
+0

Я не вижу здесь 'sendMessage'. – Jacob

+0

К сожалению, sendResponse должен быть sendMessage – Mike

+0

Я думаю, что это интересно, что я сказал, что хочу повторить попытку навсегда, но каждый ответ до сих пор был написан только для конечного числа попыток. Наверное, должно быть что-то о зацикливании навсегда, что беспокоит людей. – Mike

ответ

7

Вот пример функции повтора (пока не проверено):

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 обещание не будет на самом деле бросить, пока все повторные попытки не были исчерпаны, ваша цепочка вызовов может продолжаться.

Edit:

Конечно, вы могли бы всегда цикл навсегда, если вы предпочли:

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

'return retry (3, function() {return sendResponseAsync (response);});' может быть 'return retry (3, sendResponseAsync.bind (null, response));' – mido

+2

Да, если вы не используя древние JS. – Jacob

+0

Пожалуйста, объясните, что делает .bind (нуль, ответ) и как он работает. Я использовал bind раньше, но только в смысле .bind (this), чтобы получить доступ к области моего объекта в цепочке обещаний. – Mike

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. 
}); 
+1

, пожалуйста, не распространяйте туземцев ... или если вы это сделаете, по крайней мере, проверьте: 'if (! Promise.prototype.retry) {' –

0

Я только что выпустил https://github.com/zyklus/promise-repeat, который не повторяет обещание, пока он либо тайм-аут, или максимальное число попыток поражаются. Это позволяет написать:

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

Вы можете использовать этот promise-retry компонент с открытым исходным кодом, который RETRIES функцию, которая возвращает обещание.

Пример:

promiseRetry((retry, number) => promiseFunction().catch(retry),{retries:3}) 
.then((result) => console.log(result)).catch(err => console.log(err)) 

Вот как это работает:

const retry = require('retry'); 


const isRetryError = (err) => (err && err.code === 'RETRYPROMISE'); 

const promiseRetry = (fn, options) => { 

    const operation = retry.operation(options); 

    return new Promise((resolve, reject) => { 
     operation.attempt((number) => { 
       return fn(err => { 
        if (isRetryError(err)) { 
         err = err.retried; 
        } 
        throw {err:'Retrying', code:'RETRYPROMISE', message: err.message}; 
       }, number) 

      .then(resolve, (err) => { 
       if (isRetryError(err)) { 

        if (operation.retry(err || new Error())) { 
         return; 
        } 
       } 

       reject(err); 
      }); 
     }); 
    }); 
} 

Вы также можете использовать this NPM package для работы.

Смежные вопросы