¿Cuál es el antipatrón de construcción de promesa explícita y cómo puedo evitarlo?

May 22 2014

Estaba escribiendo un código que hace algo parecido a:

function getStuffDone(param) {           | function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */ |     return new Promise(function(resolve, reject) {
    // or = new $.Deferred() etc.        |     // using a promise constructor
    myPromiseFn(param+1)                 |         myPromiseFn(param+1)
    .then(function(val) { /* or .done */ |         .then(function(val) {
        d.resolve(val);                  |             resolve(val);
    }).catch(function(err) { /* .fail */ |         }).catch(function(err) {
        d.reject(err);                   |             reject(err);
    });                                  |         });
    return d.promise; /* or promise() */ |     });
}                                        | }

Alguien me dijo que esto se llama " antipatrón diferido " o " Promiseantipatrón de constructor " respectivamente, ¿qué tiene de malo este código y por qué se llama antipatrón ?

Respuestas

380 BenjaminGruenbaum May 22 2014 at 17:07

El antipatrón diferido (ahora antipatrón de construcción explícita) acuñado por Esailija es un antipatrón común que hacen las personas que son nuevas en las promesas, lo hice yo mismo cuando usé promesas por primera vez. El problema con el código anterior es que no utiliza el hecho de que la cadena de promesas.

Las promesas se pueden encadenar con .theny usted puede devolver las promesas directamente. Su código getStuffDonepuede reescribirse como:

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

Las promesas tienen que ver con hacer que el código asincrónico sea más legible y se comporte como un código sincrónico sin ocultar ese hecho. Las promesas representan una abstracción sobre el valor de una operación única, abstraen la noción de una declaración o expresión en un lenguaje de programación.

Solo debe usar objetos diferidos cuando está convirtiendo una API en promesas y no puede hacerlo automáticamente, o cuando está escribiendo funciones de agregación que se expresan más fácilmente de esta manera.

Citando a Esailija:

Este es el anti-patrón más común. Es fácil caer en esto cuando realmente no comprendes las promesas y las consideras emisores de eventos glorificados o utilidad de devolución de llamada. Recapitulemos: las promesas tratan de hacer que el código asincrónico retenga la mayoría de las propiedades perdidas del código sincrónico, como la sangría plana y un canal de excepción.

142 Bergi Aug 29 2014 at 20:28

¿Qué tiene de malo?

¡Pero el patrón funciona!

Eres afortunado. Desafortunadamente, probablemente no sea así, ya que probablemente olvidó algún caso de borde. En más de la mitad de las ocurrencias que he visto, el autor se ha olvidado de ocuparse del controlador de errores:

return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

Si se rechaza la otra promesa, esto pasará desapercibido en lugar de propagarse a la nueva promesa (donde se manejaría), y la nueva promesa permanece pendiente para siempre, lo que puede provocar filtraciones.

Lo mismo sucede en el caso de que su código de devolución de llamada provoque un error, por ejemplo, cuando resultno tiene un propertyy se lanza una excepción. Eso quedaría sin manejar y dejaría la nueva promesa sin resolver.

Por el contrario, el uso .then()se encarga automáticamente de estos dos escenarios y rechaza la nueva promesa cuando ocurre un error:

 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

El antipatrón diferido no solo es engorroso, sino que también es propenso a errores . Usar .then()para encadenar es mucho más seguro.

¡Pero he manejado todo!

De Verdad? Bueno. Sin embargo, esto será bastante detallado y abundante, especialmente si usa una biblioteca de promesas que admita otras funciones como cancelación o transmisión de mensajes. ¿O tal vez lo hará en el futuro, o quieres cambiar tu biblioteca por una mejor? No querrás reescribir tu código para eso.

Los métodos de las bibliotecas ( then) no solo son compatibles de forma nativa con todas las funciones, sino que también pueden tener determinadas optimizaciones. Usarlos probablemente hará que su código sea más rápido, o al menos permitirá que se optimice para futuras revisiones de la biblioteca.

¿Cómo lo evito?

Entonces, siempre que se encuentre creando manualmente un Promiseo Deferredy se involucren promesas ya existentes, verifique primero la API de la biblioteca . El antipatrón diferido a menudo lo aplican personas que ven las promesas [solo] como un patrón de observador, pero las promesas son más que devoluciones de llamada : se supone que son componibles. Cada biblioteca decente tiene muchas funciones fáciles de usar para la composición de promesas de todas las formas imaginables, ocupándose de todas las cosas de bajo nivel con las que no quiere lidiar.

Si ha encontrado la necesidad de redactar algunas promesas de una manera nueva que no es compatible con una función auxiliar existente, escribir su propia función con aplazamientos inevitables debería ser su última opción. Considere cambiar a una biblioteca con más funciones y / o presente un error en su biblioteca actual. Su responsable debe poder derivar la composición de las funciones existentes, implementar una nueva función auxiliar para usted y / o ayudar a identificar los casos extremos que deben manejarse.