¿Cuándo debería usar el método "entonces" de jQuery deferred y cuándo debería usar el método "pipe"?

97

jQuery Deferredtiene dos funciones que se pueden utilizar para implementar el encadenamiento asincrónico de funciones:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Una función, o matriz de funciones, que se llama cuando se resuelve el aplazado.
failCallbacks Una función, o conjunto de funciones, que se llama cuando se rechaza el aplazado.

pipe()

deferred.pipe( [doneFilter] [, failFilter] ) Returns: Promise

doneFilter Una función opcional que se llama cuando se resuelve el diferido.
failFilter Una función opcional que se llama cuando se rechaza el Deferred.

Sé que then()ha existido un poco más de pipe()lo que este último debe agregar algún beneficio adicional, pero cuál es la diferencia precisamente se me escapa. Ambos toman prácticamente los mismos parámetros de devolución de llamada, aunque difieren en el nombre y la diferencia entre devolver un Deferredy devolver un Promiseparece leve.

He leído los documentos oficiales una y otra vez, pero siempre los encuentro demasiado "densos" para realmente entender y la búsqueda ha encontrado mucha discusión sobre una característica u otra, pero no he encontrado nada que realmente aclare las diferentes. pros y contras de cada uno.

Entonces, ¿cuándo es mejor usar theny cuándo es mejor usar pipe?


Adición

La excelente respuesta de Felix realmente ha ayudado a aclarar en qué se diferencian estas dos funciones. Pero me pregunto si hay ocasiones en las que la funcionalidad de then()es preferible a la de pipe().

Es evidente que pipe()es más poderoso que then()y parece que el primero puede hacer cualquier cosa que el segundo pueda hacer. Una razón para usarlo then()podría ser que su nombre refleje su papel como la terminación de una cadena de funciones que procesan los mismos datos.

Pero, ¿hay un caso de uso que requiera then()devolver el original Deferredque no se puede hacer pipe()debido a que devuelve uno nuevo Promise?

hippie
fuente
1
Pensé en esto por un tiempo, pero tbh, no puedo pensar en ningún caso de uso. Podría ser una sobrecarga crear nuevos objetos de promesa si no los necesita (no sé cómo están encadenados internamente). Dicho esto, ciertamente hay personas que comprenden esto mejor que yo.
Felix Kling
6
Cualquiera que esté interesado en esta pregunta seguramente estará interesado en el Ticket # 11010 en el rastreador de errores de jQuery: HAGA
APLAZAR
1
relacionado: documentación de tubería () y luego () vs realidad en jQuery 1.8
Bergi

Respuestas:

103

Dado que jQuery 1.8 se .then comporta igual que .pipe:

Aviso de obsolescencia: a partir de jQuery 1.8, el deferred.pipe()método está obsoleto. En deferred.then()su lugar, debería utilizarse el método que lo reemplaza.

y

A partir de jQuery 1.8 , el deferred.then()método devuelve una nueva promesa que puede filtrar el estado y los valores de un diferido a través de una función, reemplazando el deferred.pipe()método ahora obsoleto .

Los ejemplos siguientes pueden resultar útiles para algunos.


Sirven para diferentes propósitos:

  • .then()se utiliza siempre que se quiera trabajar con el resultado del proceso, es decir, como dice la documentación, cuando el objeto diferido se resuelve o rechaza. Es lo mismo que usar .done()o .fail().

  • Lo usaría .pipe()para (pre) filtrar el resultado de alguna manera. El valor de retorno de una devolución de llamada se .pipe()pasará como argumento a las devoluciones de llamada doney fail. También puede devolver otro objeto diferido y las siguientes devoluciones de llamada se registrarán en este diferido.

    Ese no es el caso con .then()(o .done(), .fail()), los valores de retorno de las devoluciones de llamada registradas simplemente se ignoran.

Entonces no es que uses ni .then() o .pipe() . Usted podría utilizar .pipe()para los mismos fines que .then()pero lo contrario no se cumple.


Ejemplo 1

El resultado de alguna operación es una matriz de objetos:

[{value: 2}, {value: 4}, {value: 6}]

y desea calcular el mínimo y el máximo de los valores. Supongamos que usamos dos donedevoluciones de llamada:

deferred.then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var min = Math.min.apply(Math, values);

   /* do something with "min" */

}).then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var max = Math.max.apply(Math, values);

   /* do something with "max" */ 

});

En ambos casos, debe iterar sobre la lista y extraer el valor de cada objeto.

¿No sería mejor extraer de alguna manera los valores de antemano para que no tenga que hacer esto en ambas devoluciones de llamada individualmente? ¡Si! Y eso es lo que podemos usar .pipe()para:

deferred.pipe(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    return values; // [2, 4, 6]

}).then(function(result) {
    // result = [2, 4, 6]

    var min = Math.min.apply(Math, result);

    /* do something with "min" */

}).then(function(result) {
    // result = [2, 4, 6]

    var max = Math.max.apply(Math, result);

    /* do something with "max" */

});

Obviamente, este es un ejemplo inventado y hay muchas formas diferentes (tal vez mejores) de resolver este problema, pero espero que ilustre el punto.


Ejemplo 2

Considere las llamadas de Ajax. A veces, desea iniciar una llamada Ajax después de que se complete una anterior. Una forma es realizar la segunda llamada dentro de una donedevolución de llamada:

$.ajax(...).done(function() {
    // executed after first Ajax
    $.ajax(...).done(function() {
        // executed after second call
    });
});

Ahora supongamos que desea desacoplar su código y poner estas dos llamadas Ajax dentro de una función:

function makeCalls() {
    // here we return the return value of `$.ajax().done()`, which
    // is the same deferred object as returned by `$.ajax()` alone

    return $.ajax(...).done(function() {
        // executed after first call
        $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

Le gustaría usar el objeto diferido para permitir que otro código que llame makeCallsadjunte devoluciones de llamada para la segunda llamada Ajax, pero

makeCalls().done(function() {
    // this is executed after the first Ajax call
});

no tendría el efecto deseado ya que la segunda llamada se realiza dentro de una donedevolución de llamada y no es accesible desde el exterior.

La solución sería usar .pipe()en su lugar:

function makeCalls() {
    // here we return the return value of `$.ajax().pipe()`, which is
    // a new deferred/promise object and connected to the one returned
    // by the callback passed to `pipe`

    return $.ajax(...).pipe(function() {
        // executed after first call
        return $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

makeCalls().done(function() {
    // this is executed after the second Ajax call
});

Al usarlo .pipe(), ahora puede hacer posible agregar devoluciones de llamada a la llamada Ajax "interna" sin exponer el flujo / orden real de las llamadas.


En general, los objetos diferidos proporcionan una forma interesante de desacoplar su código :)

Felix Kling
fuente
Ah, sí, pasé por alto que pipepuede filtrar lo que thenno puede hacer. Pero al buscar en Google estos temas, parece que optaron por llamarlo en pipelugar de filterporque consideraron que el filtrado era una especie de bono extra que venía con él, mientras pipeque indicaba más claramente su verdadero propósito. Entonces parece que debería haber otras diferencias además del filtrado. (Por otra parte, admito que realmente no entiendo la función de filtrado incluso con sus ejemplos. ¿Debería result values;ser return values;por cierto?)
hippietrail
Cuando digo que no entiendo sus ejemplos, ¿es algo como esto? En el ejemplo superior, los dos .then()reciben los mismos datos en los resultque filtra cada vez; mientras que en el ejemplo inferior, el .pipe()elimina algunos de los datos en su resultantes de pasar eso como resultlos dos siguientes .then()recibirán?
hippietrail
1
@hippietrail: Actualicé mi respuesta mientras tanto y también incluí los otros propósitos de .pipe(). Si la devolución de llamada devuelve un objeto diferido, las devoluciones de llamada posteriores realizadas o fallidas se registrarán para ese objeto. Incluiré otro ejemplo. editar: con respecto a su segundo comentario: sí.
Felix Kling
Así que una diferencia es que los datos fluyen a través pipe() mientras que then()es más como un nodo de hoja al final de los cuales debe utilizar sus datos y no fluye más adelante, y que a pesar de que then()devuelve una Deferredno es realmente utilizados / útil? Si esto es correcto, podría ayudar aclarar incluir algo como /* do something with "min"/"max" */en cada "cláusula .then ()".
hippietrail
1
No te preocupes :) Me tomó algo de tiempo comprender completamente cómo funcionan los objetos diferidos y sus métodos. Pero una vez que lo entiendes, ya no parece ser difícil. Estoy de acuerdo en que la documentación probablemente podría escribirse de una manera más fácil.
Felix Kling
7

No hay ningún caso en el que DEBE usar then()más pipe(). Siempre puede optar por ignorar el valor que pipe()se transferirá. Puede haber un ligero impacto en el rendimiento para el uso pipe, pero es poco probable que importe.

Por lo tanto, podría parecer que simplemente siempre se puede usar pipe()en ambos casos. Sin embargo , al usar pipe(), le está comunicando a otras personas que leen su código (incluido usted mismo, dentro de seis meses) que hay cierta importancia en el valor de retorno. Si lo está descartando, está violando esta construcción semántica.

Es como tener una función que devuelve un valor que nunca se usa: confuso.

Así que úselo then()cuando deba y pipe()cuando deba ...

Alex Feinman
fuente
3
Encontré un ejemplo de la vida real usando los dos en el blog de K. Scott Allen, "Experiments In Writing": geolocalización, geocodificación y promesas de jQuery : "Entonces, la lógica de control se lee bastante bien:" $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); "Tenga en cuenta que la tubería es diferente a luego porque pipe devuelve una nueva promesa ".
hippietrail
5

De hecho, resulta que la diferencia entre .then()y .pipe()se ha considerado innecesaria y se ha hecho que sea la misma que la de jQuery versión 1.8.

De un comentariojaubourg en el ticket de seguimiento de errores de jQuery # 11010 "HACER APLAZADO. ENTONCES == APLAZADO.TUBO COMO PROMESA / A":

En 1.8, eliminaremos el antiguo y lo reemplazaremos con el tubo actual. Pero la consecuencia más grave es que tendremos que decirle a la gente que use lo no estándar hecho, fallar y progresar, porque la propuesta no proporciona una simple, EFICIENTE, significa simplemente agregar una devolución de llamada.

(énfasis mío)

hippie
fuente
1
La mejor referencia hasta ahora, estoy buscando usos avanzados.
TWiStErRob