write utiliza una tipos termine terminar qué que pasar parametros para lista las instrucciones hace función funciones funcion esperar ejemplo desde javascript asynchronous

utiliza - tipos de funciones en javascript



¿Por qué mi variable no se modifica después de modificarla dentro de una función?-Referencia de código asíncrono. (6)

Teniendo en cuenta los siguientes ejemplos, ¿por qué outerScopeVar no está definido en todos los casos?

var outerScopeVar; var img = document.createElement(''img''); img.onload = function() { outerScopeVar = this.width; }; img.src = ''lolcat.png''; alert(outerScopeVar);

var outerScopeVar; setTimeout(function() { outerScopeVar = ''Hello Asynchronous World!''; }, 0); alert(outerScopeVar);

// Example using some jQuery var outerScopeVar; $.post(''loldog'', function(response) { outerScopeVar = response; }); alert(outerScopeVar);

// Node.js example var outerScopeVar; fs.readFile(''./catdog.html'', function(err, data) { outerScopeVar = data; }); console.log(outerScopeVar);

// with promises var outerScopeVar; myPromise.then(function (response) { outerScopeVar = response; }); console.log(outerScopeVar);

// geolocation API var outerScopeVar; navigator.geolocation.getCurrentPosition(function (pos) { outerScopeVar = pos; }); console.log(outerScopeVar);

¿Por qué sale undefined en todos estos ejemplos? No quiero soluciones, quiero saber por qué sucede esto.

Nota: Esta es una pregunta canónica para JavaScript asynchronicity . Siéntase libre de mejorar esta pregunta y agregue más ejemplos simplificados con los que la comunidad pueda identificarse.


Aquí hay una respuesta más concisa para las personas que buscan una referencia rápida, así como algunos ejemplos que utilizan promesas y async / await.

Comience con el enfoque ingenuo (que no funciona) para una función que llama a un método asíncrono (en este caso setTimeout ) y devuelve un mensaje:

function getMessage(callback) { setTimeout(function() { callback(''Hello asynchronous world!''); }, 0); } getMessage(function(message) { console.log(message); });

undefined se registra en este caso porque getMessage regresa antes de que se setTimeout devolución de llamada de outerScopeVar y actualice outerScopeVar .

Las dos formas principales de resolverlo son mediante devoluciones de llamada y promesas :

Devoluciones de llamada

El cambio aquí es que getMessage acepta un parámetro de callback que se llamará para devolver los resultados al código de llamada una vez que esté disponible.

function getMessage() { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(''Hello asynchronous world!''); }, 0); }); } getMessage().then(function(message) { console.log(message); });

Promises

Las promesas proporcionan una alternativa que es más flexible que las devoluciones de llamada, ya que pueden combinarse naturalmente para coordinar múltiples operaciones asíncronas. Una implementación estándar de Promises/A+ se proporciona de forma nativa en node.js (0.12+) y muchos navegadores actuales, pero también se implementa en bibliotecas como Bluebird y Q

function getMessage() { var deferred = $.Deferred(); setTimeout(function() { deferred.resolve(''Hello asynchronous world!''); }, 0); return deferred.promise(); } getMessage().done(function(message) { console.log(message); });

jQuery Deferreds

jQuery proporciona una funcionalidad que es similar a las promesas con sus aplazados.

function getMessage () { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(''Hello asynchronous world!''); }, 0); }); } async function main() { let message = await getMessage(); console.log(message); } main();

asíncrono / espera

Si su entorno de JavaScript incluye soporte para async y await (como Node.js 7.6+), entonces puede usar promesas sincrónicamente dentro de las funciones async :

var outerScopeVar; //line 1 $.post(''loldog'', function(response) { //line 2 outerScopeVar = response; }); alert(outerScopeVar); //line 3


En todos estos escenarios, outerScopeVar se modifica o se le asigna un valor de forma asincrónica o que ocurre en un momento posterior (en espera o escucha que ocurra algún evento), para lo cual la ejecución actual no esperará .

Discutamos cada ejemplo (marqué la parte que se llama de forma asíncrona o retrasada para que ocurran algunos eventos):

1.

Aquí registramos un listado de eventos que se ejecutará en ese evento en particular. Aquí se carga la imagen. Luego, la ejecución actual continúa con las siguientes líneas img.src = ''lolcat.png''; y alert(outerScopeVar); Mientras tanto el evento puede no ocurrir. es decir, función img.onload espera a que se cargue la imagen referida, de forma asíncrona. Esto sucederá todo el siguiente ejemplo, el evento puede diferir.

2.

Aquí, el evento de tiempo de espera cumple la función, que invocará al controlador después del tiempo especificado. Aquí es 0 , pero aún así registra un evento asíncrono que se agregará a la última posición de la Event Queue para su ejecución, lo que hace que el retraso garantizado.

3.

Esta vez ajax callback.

4.

El nodo se puede considerar como un rey de la codificación asíncrona. Aquí la función marcada se registra como un controlador de devolución de llamada que se ejecutará después de leer el archivo especificado.

5.

La promesa obvia (algo se hará en el futuro) es asíncrona. ver ¿Cuáles son las diferencias entre diferido, promesa y futuro en JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript


Las otras respuestas son excelentes y solo quiero dar una respuesta directa a esto. Solo limitando a jQuery las llamadas asíncronas.

Todas las llamadas ajax (incluyendo $.get o $.post o $.ajax ) son asíncronas.

Considerando tu ejemplo

var outerScopeVar; //line 1 $.post(''loldog'', function(response) { //line 2, takes 10 seconds to complete outerScopeVar = response; }); alert("Lets wait for some time here! Waiting is fun"); //line 3 alert(outerScopeVar); //line 4

La ejecución del código comienza desde la línea 1, declara la variable y desencadena una llamada asíncrona en la línea 2 (es decir, la solicitud posterior) y continúa su ejecución desde la línea 3, sin esperar a que la solicitud posterior complete su ejecución.

Digamos que la solicitud posterior tarda 10 segundos en completarse, el valor de outerScopeVar solo se establecerá después de esos 10 segundos.

Para probar,

var outerScopeVar; $.post(''loldog'', function(response) { outerScopeVar = response; alert(outerScopeVar); });

Ahora, cuando ejecute esto, obtendría una alerta en la línea 3. Ahora espere un tiempo hasta que esté seguro de que la solicitud posterior ha devuelto algún valor. Luego, cuando haga clic en Aceptar, en el cuadro de alerta, la siguiente alerta imprimirá el valor esperado, porque lo esperó.

En el escenario de la vida real, el código se convierte,

var outerScopeVar; $.post(''loldog'', function(response) { outerScopeVar = response; alert(outerScopeVar); });

Todo el código que depende de las llamadas asíncronas, se mueve dentro del bloque asíncrono, o esperando las llamadas asíncronas.


Para indicar lo obvio, la copa representa outerScopeVar .

Las funciones asíncronas son como ...


Respuesta de una palabra: asincronicidad .

Prólogo

Este tema se ha repetido al menos un par de miles de veces, aquí, en Desbordamiento de pila. Por lo tanto, en primer lugar me gustaría señalar algunos recursos extremadamente útiles:

  • "Cómo devolver la respuesta de una llamada AJAX de @Felix Kling" . Vea su excelente respuesta que explica los flujos síncronos y asíncronos, así como la sección "Código de reestructuración".
    @Benjamin Gruenbaum también ha puesto mucho esfuerzo para explicar la asincronicidad en el mismo hilo.

  • La respuesta de @Matt Esch a "Obtener datos de fs.readFile" también explica la asincronicidad extremadamente bien de una manera sencilla.

La respuesta a la pregunta que nos ocupa.

Vamos a rastrear el comportamiento común primero. En todos los ejemplos, outerScopeVar se modifica dentro de una función . Esa función claramente no se ejecuta de inmediato, se asigna o pasa como un argumento. Eso es lo que llamamos una devolución de llamada .

Ahora la pregunta es, ¿cuándo se llama esa devolución de llamada?

Depende del caso. Vamos a tratar de rastrear algunos comportamientos comunes de nuevo:

  • img.onload puede llamarse en el futuro , cuando (y si) la imagen se haya cargado correctamente.
  • Puede que se llame a setTimeout en el futuro , después de que el retraso haya expirado y el tiempo de espera no haya sido cancelado por clearTimeout . Nota: incluso cuando se usa 0 como retardo, todos los navegadores tienen un límite de retardo de tiempo de espera mínimo (especificado para ser 4ms en la especificación HTML5).
  • La devolución de llamada de jQuery $.post se puede llamar en el futuro , cuando (y si) la solicitud de Ajax se haya completado con éxito.
  • fs.readFile puede llamar a fs.readFile de Node.js en el futuro , cuando el archivo se haya leído correctamente o se haya producido un error.

En todos los casos, tenemos una devolución de llamada que puede ejecutarse en el futuro . Este "en algún momento en el futuro" es lo que llamamos flujo asíncrono .

La ejecución asíncrona se expulsa del flujo síncrono. Es decir, el código asíncrono nunca se ejecutará mientras se está ejecutando la pila de códigos síncrona. Este es el significado de JavaScript siendo de un solo hilo.

Más específicamente, cuando el motor JS está inactivo, no ejecuta una pila de (a) código síncrono, sondeará los eventos que pueden haber activado devoluciones de llamada asíncronas (por ejemplo, tiempo de espera expirado, respuesta de red recibida) y los ejecutará uno tras otro. Esto es considerado como Event Loop .

Es decir, el código asíncrono resaltado en las formas rojas dibujadas a mano puede ejecutarse solo después de que se haya ejecutado todo el código síncrono restante en sus respectivos bloques de código:

En resumen, las funciones de devolución de llamada se crean de forma síncrona, pero se ejecutan de forma asíncrona. ¿No puede confiar en la ejecución de una función asíncrona hasta que sepa que se ha ejecutado y cómo hacerlo?

Es simple, de verdad. La lógica que depende de la ejecución de la función asíncrona se debe iniciar / llamar desde esta función asíncrona. Por ejemplo, mover las alert s y console.log también dentro de la función de devolución de llamada generaría el resultado esperado, porque el resultado está disponible en ese momento.

Implementando tu propia lógica de devolución de llamada

A menudo es necesario hacer más cosas con el resultado de una función asíncrona o hacer diferentes cosas con el resultado dependiendo de dónde se ha llamado la función asíncrona. Veamos un ejemplo un poco más complejo:

var outerScopeVar; helloCatAsync(); alert(outerScopeVar); function helloCatAsync() { setTimeout(function() { outerScopeVar = ''Nya''; }, Math.random() * 2000); }

Nota: estoy usando setTimeout con un retraso aleatorio como una función asíncrona genérica, el mismo ejemplo se aplica a Ajax, readFile , onload y cualquier otro flujo asíncrono.

Este ejemplo claramente tiene el mismo problema que los otros ejemplos, no espera hasta que se ejecuta la función asíncrona.

Vamos a abordarlo implementando un sistema de devolución de llamada propio. En primer lugar, nos deshacemos de ese feo outerScopeVar que es completamente inútil en este caso. Luego agregamos un parámetro que acepta un argumento de función, nuestra devolución de llamada. Cuando finaliza la operación asíncrona, llamamos a esta devolución de llamada que pasa el resultado. La implementación (por favor lea los comentarios en orden):

// 1. Call helloCatAsync passing a callback function, // which will be called receiving the result from the async operation helloCatAsync(function(result) { // 5. Received the result from the async function, // now do whatever you want with it: alert(result); }); // 2. The "callback" parameter is a reference to the function which // was passed as argument from the helloCatAsync call function helloCatAsync(callback) { // 3. Start async operation: setTimeout(function() { // 4. Finished async operation, // call the callback passing the result as argument callback(''Nya''); }, Math.random() * 2000); }

Fragmento de código del ejemplo anterior:

var outerScopeVar; var img = document.createElement(''img''); // Here we register the callback function. img.onload = function() { // Code within this function will be executed once the image has loaded. outerScopeVar = this.width; }; // But, while the image is loading, JavaScript continues executing, and // processes the following lines of JavaScript. img.src = ''lolcat.png''; alert(outerScopeVar);

En la mayoría de los casos de uso real, la API DOM y la mayoría de las bibliotecas ya proporcionan la funcionalidad de devolución de llamada (la implementación helloCatAsync en este ejemplo demostrativo). Solo necesita pasar la función de devolución de llamada y comprender que se ejecutará fuera del flujo síncrono y reestructurará su código para acomodarlo.

También notará que debido a la naturaleza asíncrona, es imposible return un valor de un flujo asíncrono al flujo síncrono donde se definió la devolución de llamada, ya que las devoluciones de llamada asíncronas se ejecutan mucho después de que el código síncrono haya terminado de ejecutarse.

En lugar de return un valor de una devolución de llamada asíncrona, tendrá que hacer uso del patrón de devolución de llamada, o ... Promesas.

Promesas

Aunque hay formas de mantener a raya el infierno de devolución de llamada con vanilla JS, las promesas están creciendo en popularidad y actualmente se están estandarizando en ES6 (consulte Promise - MDN ).

Las promesas (también conocidas como Futures) proporcionan una lectura más lineal y, por lo tanto, agradable del código asíncrono, pero la explicación de su funcionalidad completa está fuera del alcance de esta pregunta. En cambio, dejaré estos excelentes recursos para los interesados:

Más material de lectura sobre asynchronicity de JavaScript.

  • The Art of Node - Callbacks explica el código asíncrono y las devoluciones de llamada muy bien con los ejemplos de JS de vainilla y el código Node.js también.

Nota: He marcado esta respuesta como Community Wiki, por lo que cualquier persona con al menos 100 reputaciones puede editarla y mejorarla. Por favor, siéntase libre de mejorar esta respuesta, o envíe una respuesta completamente nueva si lo desea también.

Quiero que esta pregunta se convierta en un tema canónico para responder a los problemas asincrónicos que no están relacionados con Ajax (para ello, ¿cómo devolver la respuesta de una llamada AJAX? ), Por lo tanto, este tema necesita su ayuda para ser lo más útil y útil posible. !


La respuesta de Fabrício es acertada; pero quería complementar su respuesta con algo menos técnico, que se centra en una analogía para ayudar a explicar el concepto de asincronía .

Una analogía ...

Ayer, el trabajo que estaba haciendo requería cierta información de un colega. Lo llamé Así es como fue la conversación:

Yo : Hola Bob, necesito saber cómo logramos el bar la semana pasada. Jim quiere un informe al respecto, y usted es el único que conoce los detalles al respecto.

Bob : Claro, pero me tomará alrededor de 30 minutos?

Yo : Eso es genial Bob. ¡Devuélveme un anillo cuando tengas la información!

En este punto, colgué el teléfono. Como necesitaba información de Bob para completar mi informe, dejé el informe y me fui a tomar un café, y luego me puse al día con un correo electrónico. 40 minutos más tarde (Bob es lento), Bob me devolvió la llamada y me dio la información que necesitaba. En este punto, reanudé mi trabajo con mi informe, ya que tenía toda la información que necesitaba.

Imagina si la conversación hubiera sido así en su lugar;

Yo : Hola Bob, necesito saber cómo logramos el bar la semana pasada. Jim quiere un informe al respecto, y tú eres el único que sabe los detalles al respecto.

Bob : Claro, pero me tomará alrededor de 30 minutos?

Yo : Eso es genial Bob. Esperaré.

Y me senté allí y esperé. Y esperó. Y esperó. Durante 40 minutos. No haciendo nada más que esperar. Finalmente, Bob me dio la información, colgamos y completé mi informe. Pero había perdido 40 minutos de productividad.

Esto es asíncrono vs. comportamiento síncrono.

Esto es exactamente lo que está sucediendo en todos los ejemplos en nuestra pregunta. La carga de una imagen, la carga de un archivo desde el disco y la solicitud de una página a través de AJAX son operaciones lentas (en el contexto de la informática moderna).

En lugar de esperar a que se completen estas operaciones lentas, JavaScript le permite registrar una función de devolución de llamada que se ejecutará cuando la operación lenta haya finalizado. Mientras tanto, sin embargo, JavaScript continuará ejecutando otro código. El hecho de que JavaScript ejecute otro código mientras espera a que se complete la operación lenta hace que el comportamiento sea asíncrono . Si JavaScript hubiera estado esperando a que se completara la operación antes de ejecutar cualquier otro código, este habría sido un comportamiento sincrónico .

var img = document.createElement(''img''); img.onload = function() { var localScopeVar = this.width; alert(localScopeVar); }; img.src = ''lolcat.png'';

En el código anterior, le pedimos a JavaScript que cargue lolcat.png , que es una operación lenta . La función de devolución de llamada se ejecutará una vez que se haya realizado esta operación lenta, pero mientras tanto, JavaScript continuará procesando las siguientes líneas de código; es decir, alert(outerScopeVar) .

Es por esto que vemos la alerta mostrando undefined ; ya que la alert() se procesa inmediatamente, en lugar de después de que la imagen se haya cargado.

Para corregir nuestro código, todo lo que tenemos que hacer es mover el código de alert(outerScopeVar) a la función de devolución de llamada. Como consecuencia de esto, ya no necesitamos la variable outerScopeVar declarada como una variable global.

function getWidthOfImage(src) { var outerScopeVar; var img = document.createElement(''img''); img.onload = function() { outerScopeVar = this.width; }; img.src = src; return outerScopeVar; } var width = getWidthOfImage(''lolcat.png''); alert(width);

Siempre verá que una devolución de llamada se especifica como una función, porque esa es la única * manera en JavaScript de definir algún código, pero no ejecutarlo hasta más tarde.

Por lo tanto, en todos nuestros ejemplos, la function() { /* Do something */ } es la devolución de llamada; Para arreglar todos los ejemplos, ¡todo lo que tenemos que hacer es mover el código que necesita la respuesta de la operación allí!

* Técnicamente, también puedes usar eval() , pero eval() es malo para este propósito

¿Cómo mantengo a la persona que llama en espera?

Es posible que actualmente tenga algún código similar a este;

function getWidthOfImage(src, cb) { var img = document.createElement(''img''); img.onload = function() { cb(this.width); }; img.src = src; } getWidthOfImage(''lolcat.png'', function (width) { alert(width); });

Sin embargo, ahora sabemos que el return outerScopeVar ocurre inmediatamente; antes de que la función de devolución de llamada onload haya actualizado la variable. Esto lleva a que getWidthOfImage() devuelva undefined , y undefined undefined getWidthOfImage() alertas.

Para solucionar esto, debemos permitir que la función que llama a getWidthOfImage() registre una devolución de llamada, luego mover la alerta del ancho para que esté dentro de esa devolución de llamada;

function getMessage() { var outerScopeVar; setTimeout(function() { outerScopeVar = ''Hello asynchronous world!''; }, 0); return outerScopeVar; } console.log(getMessage());

... como antes, tenga en cuenta que hemos podido eliminar las variables globales (en este caso el width ).