2015-05-27 11 views
6

Sto usando la libreria di promessa Bluebird. Ho una catena di funzioni promisified come la seguente:Come puoi riprovare dopo un'eccezione in Javascript quando usi le promesse?

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

Di tanto in tanto sendMessage non riuscirà perché, diciamo, il server di rispondere alle non è disponibile. Voglio che il codice continui a cercare di rispondere all'infinito finché non ci riesce. Non puoi semplicemente racchiudere sendMessage in una presa perché in realtà non genera un'eccezione, suppongo, chiama la funzione "error" che, in questo codice promesso, è il "catch" in basso. Quindi ci deve essere un modo per "riprovare" inviare un messaggio nella sezione "catch". Il problema è che anche se ripasso in un loop nel "catch" non ho ancora modo di saltare alla catena delle promesse ed eseguire le restanti funzioni promesse. Come gestisco questo?

EDIT:

mio tentativi per un post HTTP ha finito per assomigliare a questo:

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

Non vedo 'sendMessage' qui. – Jacob

+0

Spiacente, sendResponse dovrebbe essere sendMessage – Mike

+0

Penso che sia interessante che ho detto che voglio riprovare per sempre ma ogni risposta è stata scritta per un numero limitato di tentativi. Deve esserci qualcosa sul looping per sempre che disturba le persone. – Mike

risposta

7

Ecco una funzione di esempio di tentativo (non ancora testato):

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

L'idea è che puoi avvolgere una funzione che restituisce una promessa con qualcosa che catturerà e riproverà in errore fino a esaurimento dei tentativi. Quindi, se avete intenzione di riprovare 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 
}); 

Poiché la promessa retry non sarà effettivamente buttare fino a quando tutti i tentativi sono stati esauriti, la catena chiamata può continuare.

Edit:

Naturalmente, si può sempre ciclo per sempre se avremmo preferito:

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

'return retry (3, function() {return sendResponseAsync (response);});' può essere 'return retry (3, sendResponseAsync.bind (null, response));' – mido

+2

Sì, purché non lo sia usando l'antico JS. – Jacob

+0

Si prega di spiegare cosa fa .bind (null, response) e come funziona. Ho usato bind in precedenza, ma solo nel senso di .bind (this) in modo da poter accedere all'ambito del mio oggetto all'interno della catena di promesse. – Mike

2

Ecco un piccolo aiuto che si comporta come then ma ritenta la funzione.

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

Il che permetterà di scrivere il codice più bella come:

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

per favore non estendere i nativi ... o se lo fai, controlla almeno: 'if (! Promise.prototype.retry) {' –

0

Ho appena rilasciato https://github.com/zyklus/promise-repeat, che ritenta una promessa fino a quando non sia volte fuori o di un numero massimo di tentativi vengono colpiti. Esso consente di scrivere:

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

È possibile utilizzare questo componente open source promise-retry che ritenta una funzione che restituisce una promessa.

Esempio:

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

Ecco come funziona:

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

È inoltre possibile utilizzare this NPM package per il lavoro.