¿Qué son las devoluciones de llamada diferidas?

15

Entiendo la idea de una devolución de llamada, donde paso una función a otra función y esa función luego usa la función suministrada a voluntad.

Me cuesta entender las devoluciones de llamada diferidas, incluso después de buscarlo en Google.

¿Podría alguien dar una explicación simple por favor? Programo en Ruby, pero también conozco un poco C / C ++, pero sobre todo era un programador experimentado en lenguaje ensamblador. Entonces, me pregunto si es un poco como una pila de direcciones de devolución de llamada que aparecen. Espero aprender jquery o node.js y estas devoluciones de llamada diferidas parecen integrales para ambos. Entiendo los principios básicos de enhebrado (aunque el objeto mutex me duele la cabeza;)

diez veces
fuente
¿Te refieres a los Deferredobjetos de jQuery ? ¿Se trata de algo específico de Node.js?
bfavaretto
1
No, quiero decir en general. Aunque sí quiero aprender jquery y posiblemente node.js, sentí que primero necesitaba saber qué es una devolución de llamada diferida. Leí el artículo de Wikipedia sobre devoluciones de llamada, pero no pude entender las devoluciones de llamada diferidas, que parecen intrínsecas al paradigma de la operación asincrónica que estará involucrado en estos lenguajes que lo usan.
1
Realmente estoy pidiendo la idea conceptual de devolución de llamada diferida en lugar de su implementación, lo siento si no lo dejé más claro. Di ejemplos de lenguaje más para explicar la idea que estoy tratando de aclarar y también mi experiencia en programación para que la gente sepa cómo presentar la respuesta. Muchas gracias por las respuestas hasta ahora. ¡Estoy llegando!
tentimes
Ok, creo que lo tengo ahora amigos, ¡gracias a todos! Sin embargo, no sé de qué manera hacer la respuesta. Cameron explicó el concepto de la manera más simple y eso era lo que realmente buscaba, pero otros también intervinieron y agregaron a mi conocimiento. No estoy seguro de qué manera de aceptar la respuesta, ya que soy nuevo en esto;)
tentimes

Respuestas:

4

A pedido, aquí hay comentarios presentados como respuesta:


No estoy seguro de que comprenda por completo el hecho de que las funciones en JS son objetos de primera clase y, por lo tanto, pueden almacenarse hasta que se necesiten, más allá del tiempo en que se crean.

Por ejemplo, digamos que desea escribir en un archivo, luego imprima un mensaje de registro; entonces llama a la función "write ()" (o lo que sea) y le pasa una función que emite el mensaje de registro (esta es la función de devolución de llamada diferida). "write ()" almacena internamente una referencia a la función dada, comienza a escribir en el archivo y configura su propia devolución de llamada para saber cuándo finaliza la escritura. Luego regresa antes de terminar la escritura; cuando es así, la devolución de llamada interna se llama de alguna manera (este es el trabajo del marco subyacente, en el caso de node.js, se realiza con un bucle de eventos), que luego llama a su devolución de llamada que imprime el mensaje de registro.

La parte "diferida" simplemente significa que su función de devolución de llamada no se llama de inmediato; llamarlo se difiere hasta el momento apropiado. En el caso de funciones asincrónicas como muchas de las de node.js, la devolución de llamada dada generalmente se llama cuando se completa la operación (o se produce un error).

La mayoría de las cosas son asíncronas en node.js, pero en el navegador con, por ejemplo, jQuery, la mayoría de las cosas son realmente sincrónicas (excepto, obviamente, para solicitudes AJAX). Dado que las funciones de primera clase son muy útiles en JavaScript (especialmente debido al excelente soporte de cierre), las devoluciones de llamada también se usan en todas partes en el navegador, pero no se "difieren" para operaciones síncronas (excepto en la medida en que no son llamadas inmediatamente por usted, pero luego por la función que llama).

El hecho de que el sistema subyacente esté controlado por eventos es ortogonal al uso de devoluciones de llamada diferidas; puede imaginar una versión (muy lenta) de node.js que inició un hilo para cada operación, y luego llamó a su devolución de llamada dada cuando el hilo terminó su trabajo, sin usar eventos en absoluto. Por supuesto, este es un modelo horrible, pero ilustra mi punto :-)

Cameron
fuente
8

La forma en que funciona una devolución de llamada diferida es cada vez que le agrega una devolución de llamada, esa devolución de llamada se empuja a una matriz. Luego, cuando se llama al método .resolve()o .resolveWith()en el objeto diferido, todas las .done()devoluciones de llamada en la matriz se ejecutan en orden.

Ahora podemos ver qué es un objeto diferido. Tome el fragmento a continuación como ejemplo.

var deferred = $.Deferred();
var promise = deferred.promise();

Lo que tenemos ahora es un objeto diferido, y el objeto prometido del objeto diferido. El objeto Diferido tiene todos los mismos métodos que el objeto promesa, sin embargo el objeto promesa solamente tiene los métodos .done(), .fail()y .always()que se utilizan para añadir devoluciones de llamada al objeto diferido para cada respectivo event. El objeto diferido por otro lado tiene varios otros métodos, lo más importante .resolve()y .reject(). Cuando se invocan estos métodos en el objeto diferido, se invocan todas las devoluciones de llamada. .resolve()activa las devoluciones de llamada .done()y .always()mientras que el .reject()método llama .fail()y .always()devuelve llamadas.

En general, el objeto diferido se mantiene oculto dentro de un ámbito privado, y el objeto de promesa se devuelve desde la función para que se puedan colocar devoluciones de llamada. El objeto diferido se resolverá más tarde, como después de que se complete una solicitud ajax o después de cargar una imagen, después de un setTimeout, etc. También es importante darse cuenta de que un objeto diferido solo se puede resolver una vez. Si ya está resuelto, sus devoluciones de llamada se llamarán de inmediato.

Aquí hay otro ejemplo, uno que yo uso:

function loadImage(url) {
    var def = $.Deferred(),
        img = new Image();
    $(img).on("load error",function(e){
        if (e.type === "error") {
            def.reject(url);
        }
        else {
            def.resolve(url);
        }
    });
    img.src = url;
    // return the promise object so that callbacks can
    // be defined on the deferred object.
    return def.promise();
}
loadImage("foobar.jpg").done(function(){
    alert("The image is loaded!");
}).fail(function(){
    alert("The image failed to load!");
}).always(function(){
    alert("This is always called!");
});

Para obtener más información sobre el $.Deferred()método de jQuery y los objetos diferidos, visite http://api.jquery.com/category/deferred-object/

user400654
fuente
Esto probablemente será invaluable una vez que entienda el concepto de una devolución de llamada diferida. Lo siento, pero todavía no entiendo el concepto de qué es una devolución de llamada diferida. Estoy más buscando la idea conceptual detrás de esto. Algo así como la idea de Mihia. Una vez que pueda entenderlo, entonces tal vez pueda entender js.
tentimes
3

No estoy seguro, pero creo que una devolución de llamada diferida se refiere a una devolución de llamada asincrónica, por lo que tendrá más suerte googleando para eso.

La mejor explicación que encontré fue en http://www.nodebeginner.org

Hola, probablementeExpensiveFunction (), por favor haga sus cosas, pero yo, el hilo único Node.js, no voy a esperar aquí hasta que termine, continuaré ejecutando las líneas de código debajo de usted, así que por favor tome esta callbackFunction () aquí y llámalo cuando hayas terminado de hacer tus cosas caras? ¡Gracias!"

En este ejemplo, probablementeExpensiveFunction es una función no bloqueante (o asíncrona). Esto significa que no se ejecuta de inmediato, sino que se coloca en un llamado bucle de eventos. El hilo de node.js continuará la ejecución, pero en algún momento decidirá ejecutar algo del bucle de eventos. Cuando llega a probablementeExpensiveFunction, lo llama, y ​​cuando probablementeExpensiveFunction finaliza la ejecución, llama a la devolución de llamada (diferida) que se le pasa como parámetro.

Como un ejemplo de probablementeExpensiveFunction puede tomar fs.readFile

mihai
fuente
Gracias. Pero, ¿cómo va a hacer la función costosa si ya ha regresado y está de vuelta en el hilo principal de ejecución? No entiendo nada. ¿Estás diciendo que la función persiste de alguna manera después de que termina?
Editó la respuesta, tal vez ahora esté más claro.
Muy útil. Pero ... ¿tengo que procesar este conjunto de devoluciones de llamada almacenadas? Lo que quiero decir es, ¿qué es lo que está procesando esta lista? ¿Es (por ejemplo) que js levanta estas devoluciones de llamada en segundo plano sin que tenga que hacer nada al respecto, o es que un evento hace que node.js o algo llame a una devolución de llamada en particular? En este momento, estoy recibiendo alrededor del 70% de lo que está diciendo, pero estoy un poco perdido en el resto :)
tentimes
@tentimes - "qué es lo que está procesando esta lista" el objeto $ .Deferred () está procesando la lista. Cuando llama .resolve()o .reject()en el objeto diferido original, se llama a la lista de devoluciones de llamada.
user400654
2
@tentimes: Por lo que dices, no estoy seguro de que comprendas completamente el hecho de que las funciones en JS son objetos de primera clase y, por lo tanto, se pueden almacenar hasta que se necesiten, más allá del tiempo en que se crean. Por ejemplo, digamos que desea escribir en un archivo, luego imprima un mensaje de registro; entonces llama a la función "escribir" (o lo que sea) y le pasa una función que emite el mensaje de registro. "write ()" almacena internamente una referencia a la función dada, comienza a escribir en el archivo y configura su propia devolución de llamada para saber cuándo finaliza la escritura. Luego regresa antes de terminar la escritura; cuando es así, se llama a su función.
Cameron
3

JavaScript es de un solo subproceso, por lo que no puede pensar en términos de subprocesos para comprender esto. Aquí hay un ejemplo de devoluciones de llamada regulares y asíncronas usando jQuery:

var regularCallback = function(evt) {
    alert("I'm a callback!")
}
var asyncCallback = function(data) {
    alert("I only run when an async operation finishes!")
}

// Bind the regular callback to a button's click event
$('#mybutton').on('click', regularCallback);

// Start an ajax request to the server. The request is asynchronous, so code
// below this line will execute immediately. The callback function
// will only be called when the request is complete.
$.get("http://google.com", asyncCallback);
bfavaretto
fuente
Ahora esto me está llevando a algún lado, gracias. Entonces, el asíncrono es en respuesta a un evento que configuré con ajax, que simplemente lo agota y, si ocurre el evento, se llama a mi devolución de llamada. Creo que lo estoy entendiendo. ¿No haría node.js / jquery algo similar con jquery usando objetos diferidos y un complicado sistema de interacción con ellos para realmente hacer lo mismo pero usando métodos?
tentimes
1
@tenimes, ¡Exactamente! Las devoluciones de llamada generalmente se ejecutan en respuesta a eventos (pero dado que "devolución de llamada" no es una construcción de lenguaje js, a veces el término se usa en otros contextos). Los objetos diferidos (promesas) que ves en la respuesta de Kevin son básicamente azúcar sintáctico. Se "resuelven" o "rechazan" cuando se activa algún evento (asíncrono), luego llaman a la devolución de llamada apropiada ("hecho" o "falla", luego "siempre"). Usarlos en jQuery puede hacer que el código sea más legible y permite algunos trucos adicionales (como agregar fácilmente una segunda devolución de llamada después de disparar una solicitud ajax, por ejemplo).
bfavaretto
Ambos son devoluciones de llamada asincrónicas
Raynos
1

Las devoluciones de llamada diferidas (también conocidas como Promesas ) le permiten escribir código asincrónico secuencial, sin dolor y espagueti de devolución de llamada:

$.when( doAjax(), doAnotherAjax() ).then( haveFunWithMoreAjax ).then( animateStuff );

'when' le permite esperar a que las funciones regresen en paralelo, y thenpuede encadenarse secuencialmente.

una nota: jQuery diferido ! = Promesas / A , su sintaxis es un poco diferente.

Hay buenos artículos sobre el tema: uno en IEBlog y otro en algún blog aleatorio , un libro y una pregunta popular de stackoverflow

c69
fuente
1
uhh ... resulta que OP preguntó algo ligeramente diferente ... bueno, esperemos que esta respuesta ayude a alguien más, algún día, tal vez.
c69