sincronas - ¿Por qué usar callback en JavaScript, cuáles son sus ventajas?
promise javascript (7)
¿La programación asíncrona es similar al multihilo?
sí.
El modelo asynch de Javascript proporciona una manera de hacer el trabajo "en el fondo".
Supongamos que tiene un cálculo de larga duración. Esto puede ser difícil de imaginar para una simple demostración de js, pero imagine una rutina de descompresión larga, o un algoritmo de búsqueda de trayectoria de larga ejecución en un juego, etc. algún cálculo numérico que lleve más de un segundo.
Realizar el cálculo invocando directamente la función funcionará , pero suspenderá la interfaz de usuario del navegador durante la duración del cálculo. Ejecutar cosas de forma asíncrona significa que la IU del navegador permanece receptiva, ya que el cálculo continúa ejecutándose en un subproceso en segundo plano. Cuando se completa el cálculo, de acuerdo con el patrón de programación asíncrono, la función invoca la función de devolución de llamada, notificando a la capa de aplicación que el cálculo está completo.
Alguien podría explicar, ¿por qué usamos callback en JavaScript? Encontré ejemplos, pero podrían implementarse usando las funciones normales. ¿Cuál es la ventaja de usarlo? Tengo respuestas a "cómo" usarlo y no a "por qué y cuándo" necesitamos usarlo.
Típicamente, encontré que estaba siendo usado en AJAX. en el httpRequest.onreadystatechange
. ¿Es esto similar a los subprocesos múltiples de Java? ¿Cómo y dónde está el oyente para la respuesta? ¿La programación asíncrona es similar al multihilo?
En el siguiente código, cómo es el flujo de control:
function some_function(arg1, arg2, callback) {
var my_number = Math.ceil(Math.random() * (arg1 - arg2) + arg2);
callback(my_number);
some_different_function_not_callback(arg1);
}
some_function(5, 15, function(num) {
console.log("callback called! " + num);
});
Desde el sitio web de JQuery:
Lo especial de una devolución de llamada es que las funciones que aparecen después de que el "padre" puede ejecutarse antes de que se ejecute la devolución de llamada "(ref: http://docs.jquery.com/Tutorials:How_jQuery_Works )
¿Podría alguien explicarme esta línea con un ejemplo?
Debido a que el javascript que se está ejecutando es asíncrono, por lo tanto, si solo coloca una función antigua después de realizar la solicitud asíncrona, probablemente se llamará antes de que se complete la solicitud original. La solicitud original se devolverá tan pronto como COMIENCE (se envíe), no se complete.
Si necesita hacer algo con el resultado de la solicitud asíncrona, o encadenar solicitudes, etc. necesitará una devolución de llamada para asegurarse de que el siguiente paso no comience antes de que finalice el paso anterior.
El proceso principal del navegador es un bucle de eventos de un solo hilo. Si ejecuta una operación de larga duración dentro de un bucle de eventos de un solo hilo, el proceso se "bloquea". Esto es malo porque el proceso deja de procesar otros eventos mientras espera a que se complete su operación. "alerta" es uno de los pocos métodos de bloqueo del navegador: si llama alerta ("prueba"), ya no puede hacer clic en enlaces, realizar consultas ajax o interactuar con la interfaz de usuario del navegador.
Para evitar el bloqueo en operaciones de larga ejecución, XMLHttpRequest proporciona una interfaz asíncrona. Le pasa una devolución de llamada para que se ejecute después de que se complete la operación, y mientras está procesando, cede el control al ciclo de eventos principal en lugar de bloquearlo.
No hay razón para usar una devolución de llamada a menos que desee vincular algo a un controlador de eventos, o su operación está potencialmente bloqueando y, por lo tanto, requiere una interfaz de programación asíncrona.
EDITAR: esa línea enrevesada de la documentación de jQuery solo significa que la devolución de llamada se ejecuta de forma asíncrona cuando el control se devuelve al ciclo de eventos principal.
parent_function(function () { console.log(''Callback''); });
parent_doesnt_block(); // <-- function appears after "parent"
therefore_execution_continues();
// Maybe we get ''Callback'' in the console here? or maybe later...
execution_still_continues();
Las devoluciones de llamada permiten que las operaciones de un solo hilo (Javascript es de un solo hilo) se ejecuten de forma asíncrona.
El ejemplo más obvio son las llamadas AJAX, donde tiene una devolución de llamada que se ejecuta después de que se realiza la solicitud AJAX. La solicitud de AJAX puede tardar un tiempo, y si fuera una llamada de función normal, toda la página se congelaría (no se puede desplazar, no se puede seleccionar texto, etc.) mientras se carga la solicitud.
Esto se logra a través de setTimeout
o setInterval
que pone en cola una función para ser llamada más tarde, mientras que permite que otro código se ejecute en medio. Entonces, cuando esperas a que finalice la llamada AJAX, se puede ejecutar otro código (que incluye la actualización del navegador).
Dado que desea ejemplos que no sean AJAX, el otro uso común para la naturaleza asíncrona es para animaciones. Las devoluciones de llamada son necesarias para las animaciones porque deben permitir que la interfaz de usuario dibuje.
Digamos que querías animar un div
100px a la derecha durante 5 segundos. Tu primer instinto podría decir crear un bucle y dormir en medio. Pero no hay sleep
en Javascript, e incluso si lo hubiera, simplemente congelaría la interfaz de usuario porque no puede pasar nada mientras está durmiendo.
En su lugar, debe hacer algo en la línea de incrementar la posición en 10, luego llamar a setTimeout
durante 500 ms con una devolución de llamada para ejecutar el siguiente fotograma. Esto probablemente se haría de forma recursiva.
Un uso adicional sería simplemente pasar funciones como parámetros, aunque no estoy seguro si el término "devolución de llamada" es apropiado para ese caso de uso. Así es como lo ha usado en su ejemplo, some_function
puede reutilizarse con una variedad de funciones utilizadas como devoluciones de llamada, por lo que es una especie de código de inyección.
Las devoluciones de llamada se utilizan en todo el lugar en JS, especialmente en jQuery.
Un lugar donde necesita usar una devolución de llamada es en cualquier lugar donde el idioma no le brinde coordinación. La coordinación significa que el código no se ejecuta hasta que los datos que necesita estén listos. Se coordinan llamadas sincrónicas; la llamada no vuelve hasta que finaliza el cálculo secundario. Las llamadas asíncronas no le dan coordinación, por lo que necesita una devolución de llamada.
Los cálculos controlados por eventos pueden ser descoordinados, por lo que los controladores de eventos son devoluciones de llamada:
<input id="a" /> <button id=''add''>+</button> <input id="b" /> = <span id="c"></span> <script type="text/javascript"> $(''#add'').click( function() { $(''#c'').text(parseInt($(''#a'').val()) + parseInt($(''#b'').val())); } ); </script>
Específicamente en JS, la coordinación para el envío de eventos (por ejemplo, los métodos DOM
dispatchEvent
, jQuerytrigger
y IEfireEvent
) se deja sin especificar (excepto los eventos DOM anidados , que son síncronos). Si desencadena un evento, los controladores pueden aplazarse y la ejecución volverá inmediatamente a lo que sea después del desencadenante. Los controladores de eventos generalmente se llaman de forma síncrona, pero no necesitan serlo.Los effects JQuery usualmente toman una devolución de llamada para ejecutarse una vez que terminan. Las funciones de animación son asíncronas para que no bloqueen el resto del script.
Las devoluciones de llamada también son útiles para las funciones que definen la parte externa de algunos cálculos, pero dejan una parte interna indefinida. Aquí hay unos ejemplos:
Puede utilizar devoluciones de llamada para filtrar colecciones:
// get odd items $([0,1,2,3,4,5,6,7,8,9]).filter(function (i) {return this % 2;}) // or: $.grep([0,1,2,3,4,5,6,7,8,9], function (x, i) {return x % 2;});
Array.sort
toma una devolución de llamada para que pueda definir cómo se comparan los elementos.[{name: ''foo'', id: 1}, {name: ''bar'', id: 5}, {name: ''baz'', id: 3}] .sort(function (a,b) { return a.name.localeCompare(b.name); })
Algunos de los métodos de manipulación de DOM de jQuery (como append
, append
y wrap
toman una devolución de llamada para construir un elemento basado en el contexto proporcionado por el método jQuery. Puede ver la devolución de llamada como una parte interna de un cálculo o como una cuestión de coordinación: cuando se inicia el cómputo externo, los datos necesarios para construir los nuevos elementos DOM no están disponibles, la devolución de llamada finaliza los subcálculos para crear los elementos cuando los datos estén disponibles.
setTimeout
y setInterval
toman devoluciones de llamada para ejecutarse después de un retraso.
A partir de la versión 1.5, jQuery ofrece objetos diferidos como una forma de administrar múltiples devoluciones de llamada, con varias dependencias de ejecución entre las devoluciones de llamada.
Una devolución de llamada es muy similar a una " continuation ", que básicamente significa "el resto del cálculo". La diferencia es que una continuación representa la totalidad del resto del cálculo, mientras que una devolución de llamada representa el resto de un cálculo secundario. Las continuaciones son parte de todo un estilo de programación conocido como " estilo de paso de continuación " (CPS). Con las continuaciones, puede crear todo tipo de estructuras de control interesantes. Por ejemplo, las continuaciones pueden usarse para implementar excepciones y coroutines .
Dependiendo de las características del motor de idioma (en particular, necesita una optimización de llamadas de cola ), CPS puede ofrecer un enfoque más eficiente. Algunas estructuras de control (como coroutines) requieren llamadas de cola, de lo contrario obtendrá un desbordamiento de pila * .
No es como multihilo ...
Usted utiliza una devolución de llamada en cualquier momento que necesita esperar en algo externo a su código JS primario. En un navegador, esto se usa una tonelada para AJAX, y en node.js se usa para cada cosa que llama al sistema (acceso a archivos, acceso a redes, solicitudes de base de datos, etc.).
Supongamos que desea lanzar una solicitud ajax cada vez que un usuario hace clic en un botón. Ahora digamos que la solicitud de ajax tarda 10 segundos en completarse. El usuario luego hace clic en 10 de estos botones antes de que esos 10 segundos hayan terminado. Esto llamaría repetidamente una función como esta:
var clicked = function() {
doAjax(''/some/path.json'', function(result) {
updatePageWith(result.widgets);
});
};
Esto ejecuta el código en el motor JS durante el tiempo suficiente para realizar la solicitud. Entonces ralentiza mientras espera. Otros JS pueden ejecutarse en este punto, la interfaz de usuario es totalmente fluida e interactiva, todo es increíble. Entonces, de repente, las 10 de esas solicitudes se resuelven a la vez. Y luego nuestra devolución de llamada se invoca 10 veces como magia.
Esto funciona porque cada vez que llamamos a clicked()
creamos un nuevo objeto de función y lo doAjax()
a la función doAjax()
. Por lo tanto, hay 10 objetos únicos de función de devolución de llamada en la memoria, cada uno vinculado a una solicitud específica por la función doAjax()
. Cuando se devuelve una solicitud, encuentra el objeto de devolución de llamada asociado y lo llama.
La gran ventaja aquí es que, aunque javascript es de un solo hilo, nunca atas ese hilo con la espera . Si el subproceso JS está ocupado, solo debería estarlo porque se está ejecutando activamente el código. Por lo tanto, aunque JS sea un solo hilo, es trivial que su código mantenga implícitamente el estado de cualquier número de tareas asíncronas.
El método síncrono de devoluciones de llamada se utiliza para un propósito diferente por lo general. Como oyentes o delegados. Como decirle al objeto A que devuelva la llamada cuando cambien los datos. Si bien no es estrictamente asíncrono, por lo general no está llamando a esa devolución de llamada inmediatamente. En su lugar, se llamará más tarde en respuesta a algún tipo de acción de evento del usuario.
Una función de devolución de llamada se ejecuta después de que se termina el efecto actual. Un ejemplo más detallado disponible here .