trabajar - ¿Cómo puede volver a intentar después de una excepción en Javascript cuando usa promesas?
promise javascript (4)
Estoy usando la biblioteca de la promesa Bluebird. Tengo una cadena de funciones promisificadas como la siguiente:
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
});
Ocasionalmente, sendMessage fallará porque, digamos, el servidor al que responder no está disponible. Quiero que el código siga intentando responder para siempre hasta que tenga éxito. No se puede simplemente envolver sendMessage en un catch porque no arroja una excepción, supongo, llama a la función "error" que, en este código promisificado, es la "captura" en la parte inferior. Por lo tanto, debe haber alguna forma de "volver a intentar" enviar el mensaje en la sección "captura". El problema es que incluso si vuelvo a intentar en un bucle en la "captura", todavía no tengo forma de saltar a la cadena de promesas y ejecutar las funciones prometidas restantes. ¿Cómo trato con esto?
EDITAR:
Mi intento de una publicación HTTP terminó luciendo así:
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);
});
}
Aquí hay un pequeño ayudante que actúa como then
pero vuelve a intentar la función.
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);
};
Lo cual te permitiría escribir tu código más bonito como:
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.
});
Aquí hay una función de reintento de muestra (aún no probado):
function retry(maxRetries, fn) {
return fn().catch(function(err) {
if (maxRetries <= 0) {
throw err;
}
return retry(maxRetries - 1, fn);
});
}
La idea es que puedas ajustar una función que devuelva una promesa con algo que atrape y vuelva a intentar con el error hasta que se te acaben los reintentos. Entonces, si vas a volver a intentar 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
});
Como la promesa de retry
no se lanzará hasta que se hayan agotado todos los intentos, su cadena de llamadas puede continuar.
Editar :
Por supuesto, siempre puedes repetir siempre si prefieres:
function retryForever(fn) {
return fn().catch(function(err) {
return retryForever(fn);
});
}
Acabo de lanzar https://github.com/zyklus/promise-repeat , que reintenta una promesa hasta que se agota el tiempo de espera o se alcanza un número máximo de intentos. Te permite escribir:
receiveMessageAsync(params)
...
.spread(retry(
function(response, data) {
return sendResponseAsync(response);
}
))
...
Puede utilizar este prometedor componente de código abierto que reintenta una función que devuelve una promesa.
Ejemplo:
promiseRetry((retry, number) => promiseFunction().catch(retry),{retries:3})
.then((result) => console.log(result)).catch(err => console.log(err))
Así es como funciona:
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);
});
});
});
}
También puede usar este paquete de NPM para el trabajo.