javascript - promisify - ¿Cuándo es.then(éxito, fracaso) considerado un antipatrón de promesas?
import promise from bluebird (4)
¿Cual es la diferencia?
La llamada .then()
devolverá una promesa que se rechazará en caso de que la devolución de llamada arroje un error. Esto significa que, cuando su logger
éxito falla, el error .catch()
a la siguiente devolución de llamada .catch()
, pero no a la devolución de llamada fail
que acompaña al success
.
Aquí hay un diagrama de flujo de control :
Para expresarlo en código síncrono:
// some_promise_call().then(logger.log, logger.log)
then: {
try {
var results = some_call();
} catch(e) {
logger.log(e);
break then;
} // else
logger.log(results);
}
El segundo log
(que es como el primer argumento para .then()
) solo se ejecutará en caso de que no haya ocurrido ninguna excepción. El bloque etiquetado y la declaración de break
sienten un poco raros, esto es en realidad lo que python ha try-except-else
para (¡lectura recomendada!).
// some_promise_call().then(logger.log).catch(logger.log)
try {
var results = some_call();
logger.log(results);
} catch(e) {
logger.log(e);
}
El registrador de catch
también manejará las excepciones de la llamada del registrador de éxito.
Demasiado para la diferencia.
No entiendo muy bien su explicación en cuanto a la prueba y la captura
El argumento es que generalmente quiere detectar errores en cada paso del proceso, y que no debe usarlo en cadenas. La expectativa es que solo tenga un manejador final que maneje todos los errores, mientras que cuando usa el "antipatrón", no se manejan los errores en algunas de las devoluciones de llamada.
Sin embargo, este patrón es en realidad muy útil: cuando desea manejar los errores que ocurrieron exactamente en este paso, y desea hacer algo completamente diferente cuando no ocurrió ningún error, es decir, cuando el error es irrecuperable. Tenga en cuenta que esto está ramificando su flujo de control. Por supuesto, esto a veces es deseado.
¿Qué pasa con esto lo siguiente?
some_promise_call() .then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
Que tenías que repetir tu devolución de llamada. Tú prefieres
some_promise_call()
.catch(function(e) {
return e; // it''s OK, we''ll just log it
})
.done(function(res) {
logger.log(res);
});
También podría considerar usar .finally()
para esto.
.then(success, fail)
un vistazo a la promesa de bluebird FAQ , en la que menciona que .then(success, fail)
es un antipattern . No entiendo muy bien su explicación en cuanto a la prueba y la captura. ¿Qué pasa con esto lo siguiente?
some_promise_call()
.then(function(res) { logger.log(res) }, function(err) { logger.log(err) })
Parece que el ejemplo sugiere lo siguiente como la forma correcta.
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
¿Cual es la diferencia?
Al observar las ventajas y desventajas de ambos, podemos hacer una estimación calculada de cuál es apropiado para la situación. Estos son los dos enfoques principales para implementar las promesas. Ambos tienen sus ventajas y desventajas
Enfoque de captura
some_promise_call()
.then(function(res) { logger.log(res) })
.catch(function(err) { logger.log(err) })
Ventajas
- Todos los errores son manejados por un bloque catch.
- Incluso atrapa cualquier excepción en el bloque de entonces.
- Encadenamiento de múltiples devoluciones de éxito
Desventajas
- En caso de encadenamiento, resulta difícil mostrar diferentes mensajes de error.
Enfoque de éxito / error
some_promise_call()
.then(function success(res) { logger.log(res) },
function error(err) { logger.log(err) })
Ventajas
- Obtienes control de error de grano fino.
- Puede tener una función de manejo de errores común para varias categorías de errores como error de db, error 500, etc.
Disavantages
- Todavía necesitarás otra
catch
si deseas manejar los errores lanzados por la devolución de llamada exitosa
En lugar de palabras, buen ejemplo. Código siguiente (si se ha resuelto la primera promesa):
Promise.resolve()
.then
(
() => { throw new Error(''Error occurs''); },
err => console.log(''This error is caught:'', err)
);
es idéntico a:
Promise.resolve()
.catch
(
err => console.log(''This error is caught:'', err)
)
.then
(
() => { throw new Error(''Error occurs''); }
)
Pero con la primera promesa rechazada, esto no es idéntico:
Promise.reject()
.then
(
() => { throw new Error(''Error occurs''); },
err => console.log(''This error is caught:'', err)
);
Promise.reject()
.catch
(
err => console.log(''This error is caught:'', err)
)
.then
(
() => { throw new Error(''Error occurs''); }
)
Los dos no son del todo idénticos. La diferencia es que el primer ejemplo no detectará una excepción lanzada en su controlador de success
. Por lo tanto, si su método solo devuelve promesas resueltas, como suele ser el caso, necesita un controlador de catch
posterior (o incluso otro con un parámetro de success
vacío). Claro, puede ser que su manejador de then
no haga nada que pueda fallar potencialmente, en cuyo caso usar un 2-parámetro podría estar bien.
Pero creo que el punto del texto al que vinculó es que then
su mayoría, es útil en comparación con las devoluciones de llamada en su capacidad para encadenar varios pasos asíncronos, y cuando realmente hace esto, la forma de 2 parámetros de sutilmente no se comporta del todo como se esperaba, por la razón anterior. Es particularmente contradictorio cuando se usa a mitad de la cadena.
Como alguien que ha hecho muchas cosas asincrónicas complejas y se ha topado con este tipo de situaciones más de lo que me gustaría admitir, realmente recomiendo evitar este antipatrón y usar el enfoque de controlador por separado.