→ Para obtener una explicación más general del comportamiento asíncrono con diferentes ejemplos, consulte ¿Por qué no se altera mi variable después de modificarla dentro de una función? - Referencia de código asíncrono
→ Si ya comprende el problema, pase a las posibles soluciones a continuación.
El problema
La A en Ajax significa asíncrono . Eso significa que enviar la solicitud (o más bien recibir la respuesta) se elimina del flujo de ejecución normal. En su ejemplo, $.ajax
devuelve inmediatamente y la siguiente instrucción, return result;
se ejecuta antes de que success
incluso se llamara a la función que pasó como devolución de llamada.
Aquí hay una analogía que, con suerte, hace más clara la diferencia entre flujo síncrono y asíncrono:
Sincrónico
Imagine que hace una llamada telefónica a un amigo y le pide que busque algo por usted. Aunque puede llevar un tiempo, espera en el teléfono y mira fijamente al espacio, hasta que su amigo le dé la respuesta que necesitaba.
Lo mismo sucede cuando realiza una llamada de función que contiene el código "normal":
function findItem() {
var item;
while(item_not_found) {
// search
}
return item;
}
var item = findItem();
// Do something with item
doSomethingElse();
Aunque findItem
puede llevar mucho tiempo ejecutarlo, cualquier código posterior var item = findItem();
tiene que esperar hasta que la función devuelva el resultado.
Asincrónico
Llamas a tu amigo nuevamente por la misma razón. Pero esta vez le dices que tienes prisa y que debería llamarte de nuevo a tu teléfono móvil. Cuelgas, sales de casa y haces lo que planeaste hacer. Una vez que tu amigo te devuelve la llamada, estás lidiando con la información que te dio.
Eso es exactamente lo que sucede cuando haces una solicitud de Ajax.
findItem(function(item) {
// Do something with item
});
doSomethingElse();
En lugar de esperar la respuesta, la ejecución continúa inmediatamente y la declaración después de que se ejecuta la llamada Ajax. Para obtener la respuesta, finalmente, se proporciona una función que se llama una vez la respuesta fue recibida, una devolución de llamada (aviso de algo? Posterior llamada ?). Cualquier declaración posterior a esa llamada se ejecuta antes de que se llame la devolución de llamada.
Solución (es)
¡Abrace la naturaleza asincrónica de JavaScript!Si bien ciertas operaciones asincrónicas proporcionan contrapartidas sincrónicas (también lo hace "Ajax"), generalmente se desaconseja usarlas, especialmente en un contexto de navegador.
¿Por qué es malo lo preguntas?
JavaScript se ejecuta en el hilo de la interfaz de usuario del navegador y cualquier proceso de larga duración bloqueará la interfaz de usuario, por lo que no responde. Además, hay un límite superior en el tiempo de ejecución de JavaScript y el navegador le preguntará al usuario si debe continuar la ejecución o no.
Todo esto es una experiencia de usuario realmente mala. El usuario no podrá saber si todo funciona bien o no. Además, el efecto será peor para los usuarios con una conexión lenta.
A continuación, veremos tres soluciones diferentes que se están construyendo una encima de la otra:
- Promete con
async/await
(ES2017 +, disponible en navegadores antiguos si usa un transpilador o regenerador)
- Callbacks (popular en nodo)
- Promesas con
then()
(ES2015 +, disponible en navegadores antiguos si usa una de las muchas bibliotecas de promesas)
Los tres están disponibles en los navegadores actuales y en el nodo 7+.
ES2017 +: Promesas con async/await
La versión ECMAScript lanzada en 2017 introdujo soporte de nivel de sintaxis para funciones asincrónicas. Con la ayuda de async
y await
, puede escribir de forma asíncrona en un "estilo sincrónico". El código sigue siendo asíncrono, pero es más fácil de leer / comprender.
async/await
se basa en promesas: una async
función siempre devuelve una promesa. await
"desenvuelve" una promesa y resulta en el valor con el que se resolvió la promesa o arroja un error si la promesa fue rechazada.
Importante: Solo puede usar await
dentro de una async
función. En este momento, el nivel superior await
aún no es compatible, por lo que es posible que tenga que hacer una IIFE asincrónica ( Expresión de función invocada inmediatamente ) para iniciar unasync
contexto.
Puede leer más sobre async
y await
en MDN.
Aquí hay un ejemplo que se basa en el retraso anterior:
// Using 'superagent' which will return a promise.
var superagent = require('superagent')
// This is isn't declared as `async` because it already returns a promise
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
async function getAllBooks() {
try {
// GET a list of book IDs of the current user
var bookIDs = await superagent.get('/user/books');
// wait for 3 seconds (just for the sake of this example)
await delay();
// GET information about each book
return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
} catch(error) {
// If any of the awaited promises was rejected, this catch block
// would catch the rejection reason
return null;
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let books = await getAllBooks();
console.log(books);
})();
Soporte de versiones actuales de navegador y nodoasync/await
. También puede admitir entornos más antiguos transformando su código a ES5 con la ayuda de regenerator (o herramientas que usan regenerator, como Babel ).
Dejar que las funciones acepten devoluciones de llamada
Una devolución de llamada es simplemente una función pasada a otra función. Esa otra función puede llamar a la función pasada siempre que esté lista. En el contexto de un proceso asincrónico, la devolución de llamada se llamará siempre que se realice el proceso asincrónico. Por lo general, el resultado se pasa a la devolución de llamada.
En el ejemplo de la pregunta, puede hacer que foo
acepte una devolución de llamada y usarla como success
devolución de llamada. Así que esto
var result = foo();
// Code that depends on 'result'
se convierte
foo(function(result) {
// Code that depends on 'result'
});
Aquí definimos la función "en línea" pero puede pasar cualquier referencia de función:
function myCallback(result) {
// Code that depends on 'result'
}
foo(myCallback);
foo
en sí se define de la siguiente manera:
function foo(callback) {
$.ajax({
// ...
success: callback
});
}
callback
se referirá a la función a la que pasamos foo
cuando la llamamos y simplemente la pasamos a success
. Es decir, una vez que la solicitud de Ajax sea exitosa, $.ajax
llamará callback
y pasará la respuesta a la devolución de llamada (que se puede consultar conresult
, ya que así es como definimos la devolución de llamada).
También puede procesar la respuesta antes de pasarla a la devolución de llamada:
function foo(callback) {
$.ajax({
// ...
success: function(response) {
// For example, filter the response
callback(filtered_response);
}
});
}
Es más fácil escribir código usando devoluciones de llamada de lo que parece. Después de todo, JavaScript en el navegador depende en gran medida de los eventos (eventos DOM). Recibir la respuesta de Ajax no es más que un evento.
Pueden surgir dificultades cuando tiene que trabajar con código de terceros, pero la mayoría de los problemas se pueden resolver con solo pensar en el flujo de la aplicación.
ES2015 +: Promesas con then ()
La promesa de la API es una nueva característica de ECMAScript 6 (ES2015), pero tiene buen soporte de los navegadores ya. También hay muchas bibliotecas que implementan la API Promises estándar y proporcionan métodos adicionales para facilitar el uso y la composición de funciones asincrónicas (por ejemplo, bluebird ).
Las promesas son contenedores para valores futuros . Cuando la promesa recibe el valor (se resuelve ) o cuando se cancela ( rechaza ), notifica a todos sus "oyentes" que desean acceder a este valor.
La ventaja sobre las devoluciones de llamada simples es que le permiten desacoplar su código y son más fáciles de componer.
Aquí hay un ejemplo simple de uso de una promesa:
function delay() {
// `delay` returns a promise
return new Promise(function(resolve, reject) {
// Only `delay` is able to resolve or reject the promise
setTimeout(function() {
resolve(42); // After 3 seconds, resolve the promise with value 42
}, 3000);
});
}
delay()
.then(function(v) { // `delay` returns a promise
console.log(v); // Log the value once it is resolved
})
.catch(function(v) {
// Or do something else if it is rejected
// (it would not happen in this example, since `reject` is not called).
});
Aplicado a nuestra llamada Ajax, podríamos usar promesas como esta:
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(this.responseText);
};
xhr.onerror = reject;
xhr.open('GET', url);
xhr.send();
});
}
ajax("/echo/json")
.then(function(result) {
// Code depending on result
})
.catch(function() {
// An error occurred
});
Describir todas las ventajas que promete ofrecer está más allá del alcance de esta respuesta, pero si escribe un código nuevo, debería considerarlas seriamente. Proporcionan una gran abstracción y separación de su código.
Más información sobre promesas: HTML5 rocks - JavaScript Promises
Nota al margen: los objetos diferidos de jQuery
Los objetos diferidos son la implementación personalizada de promesas de jQuery (antes de que la API Promise se estandarizara). Se comportan casi como promesas, pero exponen una API ligeramente diferente.
Todos los métodos Ajax de jQuery ya devuelven un "objeto diferido" (en realidad una promesa de un objeto diferido) que puede devolver de su función:
function ajax() {
return $.ajax(...);
}
ajax().done(function(result) {
// Code depending on result
}).fail(function() {
// An error occurred
});
Nota al margen: Promesas Gotchas
Tenga en cuenta que las promesas y los objetos diferidos son solo contenedores para un valor futuro, no son el valor en sí. Por ejemplo, suponga que tiene lo siguiente:
function checkPassword() {
return $.ajax({
url: '/password',
data: {
username: $('#username').val(),
password: $('#password').val()
},
type: 'POST',
dataType: 'json'
});
}
if (checkPassword()) {
// Tell the user they're logged in
}
Este código no comprende los problemas de asincronía anteriores. Específicamente, $.ajax()
no congela el código mientras verifica la página '/ contraseña' en su servidor: envía una solicitud al servidor y mientras espera, inmediatamente devuelve un objeto jQuery Ajax diferido, no la respuesta del servidor. Eso significa que la if
declaración siempre obtendrá este objeto diferido, lo tratará como true
y procederá como si el usuario hubiera iniciado sesión. No es bueno.
Pero la solución es fácil:
checkPassword()
.done(function(r) {
if (r) {
// Tell the user they're logged in
} else {
// Tell the user their password was bad
}
})
.fail(function(x) {
// Tell the user something bad happened
});
No recomendado: llamadas síncronas "Ajax"
Como mencioné, algunas (!) Operaciones asincrónicas tienen contrapartidas sincrónicas. No recomiendo su uso, pero para completar, así es como realizaría una llamada síncrona:
Sin jQuery
Si usa directamente un XMLHTTPRequest
objeto, pase false
como tercer argumento a .open
.
jQuery
Si usa jQuery , puede establecer la async
opción en false
. Tenga en cuenta que esta opción está en desuso desde jQuery 1.8. A continuación, puede seguir utilizando una success
devolución de llamada o acceder a la responseText
propiedad del objeto jqXHR :
function foo() {
var jqXHR = $.ajax({
//...
async: false
});
return jqXHR.responseText;
}
Si usa cualquier otro método jQuery Ajax, como $.get
, $.getJSON
etc., debe cambiarlo a $.ajax
(ya que solo puede pasar parámetros de configuración a $.ajax
).
¡Aviso! No es posible realizar una solicitud JSONP sincrónica . JSONP, por su propia naturaleza, siempre es asíncrono (una razón más para ni siquiera considerar esta opción).
If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax.
(Sí, me doy cuenta de que mi nick es un poco irónico en este caso)foo
se llama y se le pasa una función (foo(function(result) {....});
)?result
se utiliza dentro de esta función y es la respuesta de la solicitud de Ajax. Para referirse a esta función, se llama al primer parámetro de foocallback
y se le asigna ensuccess
lugar de una función anónima. Entonces,$.ajax
llamarácallback
cuando la solicitud haya sido exitosa. Traté de explicarlo un poco más.$.getJSON
si desea que la solicitud de Ajax sea sincrónica. Sin embargo, no debe desear que la solicitud sea síncrona, por lo que no se aplica. Debería usar devoluciones de llamada o promesas para manejar la respuesta, como se explica anteriormente en la respuesta.Si estás no usando jQuery en el código, esta respuesta es para usted
Su código debe ser algo similar a esto:
Felix Kling hizo un buen trabajo escribiendo una respuesta para las personas que usan jQuery para AJAX, he decidido proporcionar una alternativa para las personas que no lo son.
( Nota, para aquellos que usan la nueva
fetch
API, Angular o promesas, he agregado otra respuesta a continuación )A lo que te enfrentas
Este es un breve resumen de "Explicación del problema" de la otra respuesta, si no está seguro después de leer esto, léalo.
La A en AJAX significa asíncrono . Eso significa que enviar la solicitud (o más bien recibir la respuesta) se elimina del flujo de ejecución normal. En su ejemplo,
.send
devuelve inmediatamente y la siguiente instrucción,return result;
se ejecuta antes de quesuccess
incluso se llamara a la función que pasó como devolución de llamada.Esto significa que cuando regresa, el oyente que ha definido aún no se ejecutó, lo que significa que el valor que está devolviendo no se ha definido.
Aquí hay una analogía simple
(Violín)
El valor de
a
return esundefined
porque laa=5
parte aún no se ha ejecutado. AJAX actúa así, está devolviendo el valor antes de que el servidor tenga la oportunidad de decirle a su navegador cuál es ese valor.Una posible solución a este problema es codificar de forma reactiva , diciéndole a su programa qué hacer cuando se complete el cálculo.
Esto se llama CPS . Básicamente, estamos pasando
getFive
una acción a realizar cuando se completa, le estamos diciendo a nuestro código cómo reaccionar cuando se completa un evento (como nuestra llamada AJAX, o en este caso el tiempo de espera).El uso sería:
Lo que debería alertar "5" a la pantalla. (Violín) .
Soluciones posibles
Básicamente, hay dos formas de resolver esto:
1. AJAX sincrónico: ¡no lo hagas!
En cuanto a AJAX síncrono, ¡no lo hagas! La respuesta de Felix plantea algunos argumentos convincentes sobre por qué es una mala idea. Para resumir, congelará el navegador del usuario hasta que el servidor devuelva la respuesta y cree una experiencia de usuario muy mala. Aquí hay otro breve resumen tomado de MDN sobre por qué:
Si tiene que hacerlo, puede pasar una bandera: así es como:
2. Código de reestructuración
Deje que su función acepte una devolución de llamada. En el ejemplo,
foo
se puede hacer que el código acepte una devolución de llamada. Le diremos a nuestro código cómo reaccionar cuando sefoo
complete.Entonces:
Se convierte en:
Aquí pasamos una función anónima, pero podríamos pasar fácilmente una referencia a una función existente, haciendo que se vea así:
Para obtener más detalles sobre cómo se hace este tipo de diseño de devolución de llamada, consulte la respuesta de Felix.
Ahora, definamos foo para actuar en consecuencia
(violín)
Ahora hemos hecho que nuestra función foo acepte una acción para ejecutarse cuando el AJAX se complete con éxito, podemos extender esto más allá verificando si el estado de respuesta no es 200 y actuando en consecuencia (cree un controlador de fallas y tal). Resolviendo efectivamente nuestro problema.
Si aún le cuesta entender esto, lea la guía de inicio de AJAX en MDN.
fuente
onload
controlador, que solo se activa cuandoreadyState
está4
. Por supuesto, no es compatible con IE8. (IIRC, puede necesitar confirmación.)XMLHttpRequest 2 (en primer lugar, lea las respuestas de Benjamin Gruenbaum y Felix Kling )
Si no usa jQuery y desea un buen XMLHttpRequest 2 que funcione en los navegadores modernos y también en los navegadores móviles, sugiero que lo use de esta manera:
Como puedes ver:
Hay dos formas de obtener la respuesta de esta llamada Ajax (tres usando el nombre var XMLHttpRequest):
Lo más simple:
O si por alguna razón le
bind()
devuelven la llamada a una clase:Ejemplo:
O (lo anterior es mejor, las funciones anónimas siempre son un problema):
Nada mas facil.
Ahora, algunas personas probablemente dirán que es mejor usar onreadystatechange o incluso el nombre de variable XMLHttpRequest. Eso está mal.
Consulte las funciones avanzadas de XMLHttpRequest
Soporta todos los * navegadores modernos. Y puedo confirmar que estoy usando este enfoque ya que XMLHttpRequest 2 existe. Nunca tuve ningún tipo de problema en todos los navegadores que uso.
onreadystatechange solo es útil si desea obtener los encabezados en el estado 2.
El uso del
XMLHttpRequest
nombre de la variable es otro gran error, ya que necesita ejecutar la devolución de llamada dentro de los cierres onload / oreadystatechange; de lo contrario, lo perdió.Ahora, si desea algo más complejo usando post y FormData, puede extender fácilmente esta función:
Una vez más ... es una función muy corta, pero obtiene y publica.
Ejemplos de uso:
O pasar un elemento de forma completa (
document.getElementsByTagName('form')[0]
):O establezca algunos valores personalizados:
Como puede ver, no implementé la sincronización ... es algo malo.
Habiendo dicho eso ... ¿por qué no hacerlo de la manera fácil?
Como se mencionó en el comentario, el uso de error && synchronous rompe por completo el punto de la respuesta. ¿Cuál es una buena forma de usar Ajax de la manera adecuada?
Controlador de errores
En la secuencia de comandos anterior, tiene un controlador de errores que está estáticamente definido para que no comprometa la función. El controlador de errores también se puede utilizar para otras funciones.
Pero para sacar realmente un error, la única forma es escribir una URL incorrecta, en cuyo caso cada navegador arroja un error.
Los controladores de errores pueden ser útiles si configura encabezados personalizados, establece el tipo de respuesta en búfer de matriz de blobs o lo que sea ...
Incluso si pasa 'POSTAPAPAP' como método, no arrojará un error.
Incluso si pasa 'fdggdgilfdghfldj' como formdata, no arrojará un error.
En el primer caso, el error está dentro de
displayAjax()
debajothis.statusText
comoMethod not Allowed
.En el segundo caso, simplemente funciona. Debe verificar en el lado del servidor si pasó los datos de publicación correctos.
el dominio cruzado no permitido arroja el error automáticamente.
En la respuesta de error, no hay códigos de error.
Solo existe el
this.type
que está configurado como error.¿Por qué agregar un controlador de errores si no tiene control sobre los errores? La mayoría de los errores se devuelven dentro de esto en la función de devolución de llamada
displayAjax()
.Por lo tanto: no es necesario realizar comprobaciones de errores si puede copiar y pegar la URL correctamente. ;)
PD: Como la primera prueba, escribí x ('x', displayAjax) ..., y obtuve una respuesta ... ¿??? Así que verifiqué la carpeta donde se encuentra el HTML y había un archivo llamado 'x.xml'. Entonces, incluso si olvida la extensión de su archivo, XMLHttpRequest 2 LO ENCONTRARÁ . Yo jajaja
Leer un archivo sincrónico
No hagas eso.
Si desea bloquear el navegador por un tiempo, cargue un gran
.txt
archivo síncrono.Ahora puedes hacer
No hay otra forma de hacer esto de una manera no asincrónica. (Sí, con el ciclo setTimeout ... pero en serio?)
Otro punto es ... si trabaja con API o solo los archivos de su propia lista o lo que sea, siempre usa diferentes funciones para cada solicitud ...
Solo si tiene una página donde carga siempre el mismo XML / JSON o lo que sea, solo necesita una función. En ese caso, modifique un poco la función Ajax y reemplace b con su función especial.
Las funciones anteriores son para uso básico.
Si desea EXTENDER la función ...
Sí tu puedes.
Estoy usando muchas API y una de las primeras funciones que integro en cada página HTML es la primera función Ajax en esta respuesta, solo con GET ...
Pero puedes hacer muchas cosas con XMLHttpRequest 2:
Hice un administrador de descargas (usando rangos en ambos lados con currículum, lector de archivos, sistema de archivos), varios convertidores de redimensionadores de imágenes usando lienzo, poblar bases de datos SQL web con imágenes base64 y mucho más ... Pero en estos casos, debe crear una función solo para eso propósito ... a veces necesitas un blob, búferes de matriz, puedes configurar encabezados, anular el tipo mime y hay mucho más ...
Pero la pregunta aquí es cómo devolver una respuesta Ajax ... (agregué una manera fácil).
fuente
x
,ajax
oxhr
podría ser mejor :)). No veo cómo se trata de devolver la respuesta de una llamada AJAX. (alguien aún podría hacerlovar res = x("url")
y no entender por qué no funciona;)). En una nota al margen: sería genial si volvierasc
del método para que los usuarios puedan conectarse,error
etc.2.ajax is meant to be async.. so NO var res=x('url')..
Ese es el punto de esta pregunta y respuestas :)Si está usando promesas, esta respuesta es para usted.
Esto significa AngularJS, jQuery (con diferido), reemplazo nativo de XHR (búsqueda), EmberJS, guardado de BackboneJS o cualquier biblioteca de nodos que devuelva promesas.
Su código debe ser algo similar a esto:
Felix Kling hizo un buen trabajo escribiendo una respuesta para las personas que usan jQuery con devoluciones de llamada para AJAX. Tengo una respuesta para XHR nativo. Esta respuesta es para el uso genérico de las promesas, ya sea en la interfaz o en el backend.
El problema central
El modelo de concurrencia de JavaScript en el navegador y en el servidor con NodeJS / io.js es asíncrono y reactivo .
Cada vez que llama a un método que devuelve una promesa, los
then
controladores siempre se ejecutan de forma asíncrona, es decir, después del código debajo de ellos que no está en un.then
controlador.Esto significa que cuando devuelva
data
elthen
controlador que ha definido aún no se ejecutó. Esto a su vez significa que el valor que está devolviendo no se ha establecido en el valor correcto a tiempo.Aquí hay una analogía simple para el problema:
El valor de
data
esundefined
desde eldata = 5
parte aún no se ha ejecutado. Probablemente se ejecutará en un segundo, pero para ese momento ya no es relevante para el valor devuelto.Dado que la operación aún no sucedió (AJAX, llamada al servidor, IO, temporizador), está devolviendo el valor antes de que la solicitud tenga la oportunidad de decirle a su código cuál es ese valor.
Una posible solución a este problema es codificar de forma reactiva , diciéndole a su programa qué hacer cuando se complete el cálculo. Las promesas permiten esto activamente al ser temporal (sensible al tiempo) en la naturaleza.
Resumen rápido de las promesas
Una promesa es un valor en el tiempo . Las promesas tienen estado, comienzan como pendientes sin valor y pueden conformarse con:
Una promesa solo puede cambiar de estado una vez, después de lo cual siempre permanecerá en el mismo estado para siempre. Puede adjuntar
then
controladores a las promesas para extraer su valor y manejar los errores.then
Los manejadores permiten el encadenamiento de llamadas. Las promesas se crean mediante el uso de API que las devuelven . Por ejemplo, el reemplazo más moderno de AJAXfetch
o las$.get
promesas de devolución de jQuery .Cuando pedimos
.then
una promesa y devolvemos algo de ella, recibimos una promesa por el valor procesado . Si devolvemos otra promesa, obtendremos cosas increíbles, pero sostengamos nuestros caballos.Con promesas
Veamos cómo podemos resolver el problema anterior con promesas. Primero, demostremos nuestra comprensión de los estados de promesa desde arriba usando el constructor Promise para crear una función de retraso:
Ahora, después de convertir setTimeout para usar promesas, podemos usarlo
then
para que cuente:Básicamente, en lugar de devolver un valor que no se puede hacer debido a la modelo de concurrencia - estamos devolviendo un envoltorio para un valor que podemos desenvolver con
then
. Es como una caja con la que puedes abrirthen
.Aplicando esto
Esto es lo mismo para su llamada API original, puede:
Entonces esto funciona igual de bien. Hemos aprendido que no podemos devolver valores de llamadas ya asincrónicas, pero podemos usar promesas y encadenarlas para realizar el procesamiento. Ahora sabemos cómo devolver la respuesta de una llamada asincrónica.
ES2015 (ES6)
ES6 introduce generadores que son funciones que pueden regresar en el medio y luego reanudar el punto en el que estaban. Esto suele ser útil para secuencias, por ejemplo:
Es una función que devuelve un iterador sobre la secuencia
1,2,3,3,3,3,....
que se puede iterar. Si bien esto es interesante por sí solo y abre muchas posibilidades, hay un caso interesante en particular.Si la secuencia que estamos produciendo es una secuencia de acciones en lugar de números, podemos pausar la función cada vez que se produce una acción y esperarla antes de reanudar la función. Entonces, en lugar de una secuencia de números, necesitamos una secuencia de futuro valores , es decir, promesas.
Este truco algo complicado pero muy poderoso nos permite escribir código asincrónico de manera sincrónica. Hay varios "corredores" que hacen esto por usted, escribir uno es unas pocas líneas de código pero está más allá del alcance de esta respuesta. Usaré Bluebird's
Promise.coroutine
aquí, pero hay otros envoltorios comoco
oQ.async
.Este método devuelve una promesa en sí, que podemos consumir de otras corutinas. Por ejemplo:
ES2016 (ES7)
En ES7, esto está más estandarizado, hay varias propuestas en este momento, pero en todas puedes
await
prometer. Esto es solo "azúcar" (mejor sintaxis) para la propuesta ES6 anterior agregando las palabras claveasync
yawait
. Haciendo el ejemplo anterior:Todavía devuelve una promesa igual :)
fuente
return await data.json();
?)Estás utilizando Ajax incorrectamente. La idea no es que devuelva nada, sino que entregue los datos a algo llamado función de devolución de llamada, que maneja los datos.
Es decir:
Devolver cualquier cosa en el controlador de envío no hará nada. En su lugar, debe entregar los datos o hacer lo que quiera con ellos directamente dentro de la función de éxito.
fuente
success: handleData
y funcionaría.success
método, es el alcance que lo rodea$.ajax
.La solución más simple es crear una función de JavaScript y llamarla para la
success
devolución de llamada de Ajax .fuente
Contestaré con un cómic de aspecto horrible y dibujado a mano. La segunda imagen es la razón por la cual
result
estáundefined
en su ejemplo de código.fuente
Angular1
Para las personas que usan AngularJS , pueden manejar esta situación usando
Promises
.Aquí dice
Puedes encontrar una buena explicación aquí también.
Ejemplo encontrado en los documentos mencionados a continuación.
Angular2 y posterior
Con
Angular2
mira el siguiente ejemplo, pero se recomienda usarObservables
conAngular2
.}
Puedes consumir eso de esta manera,
Vea la publicación original aquí. Pero Typecript no es compatible con las promesas nativas de es6 , si desea usarlo, es posible que necesite un complemento para eso.
Además, aquí están las promesas que las especificaciones definen aquí.
fuente
La mayoría de las respuestas aquí dan sugerencias útiles para cuando tiene una sola operación asíncrona, pero a veces, esto surge cuando necesita hacer una operación asincrónica para cada entrada en una matriz u otra estructura similar a una lista. La tentación es hacer esto:
Ejemplo:
Mostrar fragmento de código
La razón por la que no funciona es que las devoluciones de llamada
doSomethingAsync
aún no se han ejecutado cuando intenta usar los resultados.Entonces, si tiene una matriz (o una lista de algún tipo) y desea realizar operaciones asíncronas para cada entrada, tiene dos opciones: Realizar las operaciones en paralelo (superposición) o en serie (una tras otra en secuencia).
Paralela
Puede iniciarlos todos y realizar un seguimiento de la cantidad de devoluciones de llamada que espera, y luego usar los resultados cuando haya recibido tantas devoluciones de llamada:
Ejemplo:
Mostrar fragmento de código
(Podríamos eliminar
expecting
y simplemente usarresults.length === theArray.length
, pero eso nos deja abiertos a la posibilidad de quetheArray
se cambie mientras las llamadas están pendientes ...)Observe cómo usamos
index
fromforEach
para guardar el resultado enresults
la misma posición que la entrada con la que se relaciona, incluso si los resultados llegan fuera de orden (dado que las llamadas asíncronas no necesariamente se completan en el orden en que se iniciaron).Pero, ¿qué pasa si necesita devolver esos resultados de una función? Como las otras respuestas han señalado, no puedes; debe hacer que su función acepte y llame una devolución de llamada (o devuelva una Promesa ). Aquí hay una versión de devolución de llamada:
Ejemplo:
Mostrar fragmento de código
O aquí hay una versión que devuelve un
Promise
en su lugar:Por supuesto, si
doSomethingAsync
nos pasaran errores, usaríamosreject
para rechazar la promesa cuando recibimos un error).Ejemplo:
Mostrar fragmento de código
(O, alternativamente, puede hacer un envoltorio para
doSomethingAsync
eso devuelve una promesa, y luego hacer lo siguiente ...)Si
doSomethingAsync
le da una promesa , puede usarPromise.all
:Si sabe que
doSomethingAsync
ignorará un segundo y tercer argumento, puede pasarlo directamente amap
(map
llama a su devolución de llamada con tres argumentos, pero la mayoría de las personas solo usa el primero la mayor parte del tiempo):Ejemplo:
Mostrar fragmento de código
Tenga en cuenta que
Promise.all
resuelve su promesa con una serie de resultados de todas las promesas que le hace cuando se resuelven todas, o rechaza su promesa cuando la primera de las promesas que le da rechaza.Serie
¿Y si no quieres que las operaciones sean paralelas? Si desea ejecutarlos uno tras otro, debe esperar a que se complete cada operación antes de comenzar la siguiente. Aquí hay un ejemplo de una función que hace eso y llama a una devolución de llamada con el resultado:
(Como estamos haciendo el trabajo en serie, podemos usarlo
results.push(result)
porque sabemos que no obtendremos resultados fuera de orden. En lo anterior podríamos haber usadoresults[index] = result;
, pero en algunos de los siguientes ejemplos no tenemos un índice usar.)Ejemplo:
Mostrar fragmento de código
(O, de nuevo, construya un envoltorio para
doSomethingAsync
eso le promete y haga lo siguiente ...)Si
doSomethingAsync
le da una promesa, si puede usar la sintaxis ES2017 + (tal vez con un transpilador como Babel ), puede usar unaasync
función confor-of
yawait
:Ejemplo:
Mostrar fragmento de código
Si no puede usar la sintaxis ES2017 + (todavía), puede usar una variación en el patrón " Reducción de promesa" (esto es más complejo que la reducción de Promesa habitual porque no estamos pasando el resultado de uno a otro, sino que reuniendo sus resultados en una matriz):
Ejemplo:
Mostrar fragmento de código
... que es menos engorroso con las funciones de flecha ES2015 + :
Ejemplo:
Mostrar fragmento de código
fuente
if (--expecting === 0)
parte del código, por favor? La versión de devolución de llamada de su solución está funcionando muy bien para mí, simplemente no entiendo cómo, con esa declaración, está verificando la cantidad de respuestas completadas. Aprecio que es solo falta de conocimiento de mi parte. ¿Hay alguna forma alternativa de escribir ese cheque?expecting
comienza con el valor dearray.length
, que es cuántas solicitudes vamos a hacer. Sabemos que la devolución de llamada no se llamará hasta que se inicien todas esas solicitudes. En la devolución de llamada,if (--expecting === 0)
hace esto: 1. Disminucionesexpecting
(hemos recibido una respuesta, por lo que esperamos una respuesta menos) y si el valor después de la disminución es 0 (no esperamos más respuestas), estamos ¡hecho!results
no existía). :-) Arreglado.Echa un vistazo a este ejemplo:
Como puede ver,
getJoke
está devolviendo una promesa resuelta (se resuelve al regresarres.data.value
). Por lo tanto, espere hasta que se complete la solicitud $ http.get y luego se ejecute console.log (res.joke) (como un flujo asincrónico normal).Este es el plnkr:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
Forma ES6 (asíncrono - espera)
fuente
Este es uno de los lugares que vinculan los datos de dos maneras o almacenan el concepto que se utilizan en muchos nuevos marcos de JavaScript funcionarán muy bien para usted ...
Entonces, si está utilizando Angular, React o cualquier otro marco que haga dos formas de vincular datos o almacenar el concepto, este problema simplemente se soluciona para usted, por lo que, en pocas palabras, su resultado está
undefined
en la primera etapa, por lo que tieneresult = undefined
antes de recibir el datos, luego, tan pronto como obtenga el resultado, se actualizará y se asignará al nuevo valor cuya respuesta de su llamada Ajax ...Pero, ¿cómo puedes hacerlo en javascript puro o jQuery? por ejemplo, como hizo en esta pregunta?
Puede usar una devolución de llamada , promesa y recientemente observable para manejarlo por usted, por ejemplo, en promesas que tenemos alguna función como
success()
othen()
que se ejecutará cuando sus datos estén listos para usted, lo mismo con la función de devolución de llamada o suscripción en observable .Por ejemplo, en su caso en el que está utilizando jQuery , puede hacer algo como esto:
Para obtener más información, estudie las promesas y los observables, que son nuevas formas de hacer esto de manera asíncrona.
fuente
$.ajax({url: "api/data", success: fooDone.bind(this)});
Es un problema muy común que enfrentamos mientras luchamos con los 'misterios' de JavaScript. Déjame intentar desmitificar este misterio hoy.
Comencemos con una simple función de JavaScript:
Esa es una simple llamada de función síncrona (donde cada línea de código está 'terminada con su trabajo' antes de la siguiente en secuencia), y el resultado es el mismo que se esperaba.
Ahora agreguemos un poco de giro, introduciendo un pequeño retraso en nuestra función, para que todas las líneas de código no estén 'terminadas' en secuencia. Por lo tanto, emulará el comportamiento asincrónico de la función:
¡Ahí tienes, ese retraso simplemente rompió la funcionalidad que esperábamos! ¿Pero qué pasó exactamente? Bueno, en realidad es bastante lógico si nos fijamos en el código. la función
foo()
, después de la ejecución, no devuelve nada (por lo tanto, el valor devuelto esundefined
), pero inicia un temporizador, que ejecuta una función después de 1s para devolver 'wohoo'. Pero como puede ver, el valor asignado a la barra es el material devuelto de inmediato por foo (), que no es nada, es decir, soloundefined
.Entonces, ¿cómo abordamos este problema?
Pidamos a nuestra función una PROMESA . Promise se trata realmente de lo que significa: significa que la función le garantiza proporcionar cualquier salida que obtenga en el futuro. así que vamos a verlo en acción para nuestro pequeño problema anterior:
Por lo tanto, el resumen es: para abordar las funciones asincrónicas como llamadas basadas en ajax, etc., puede usar una promesa sobre
resolve
el valor (que tiene la intención de devolver). Por lo tanto, en resumen, resuelve el valor en lugar de devolverlo en funciones asincrónicas.ACTUALIZACIÓN (Promesas con asíncrono / espera)
Además de usar
then/catch
para trabajar con promesas, existe un enfoque más. La idea es reconocer una función asincrónica y luego esperar a que se resuelvan las promesas , antes de pasar a la siguiente línea de código. Sigue siendo solo elpromises
debajo del capó, pero con un enfoque sintáctico diferente. Para aclarar las cosas, puede encontrar una comparación a continuación:entonces / captura versión:
versión asíncrona / espera:
fuente
Otro enfoque para devolver un valor de una función asincrónica es pasar un objeto que almacenará el resultado de la función asincrónica.
Aquí hay un ejemplo de lo mismo:
Estoy usando el
result
objeto para almacenar el valor durante la operación asincrónica. Esto permite que el resultado esté disponible incluso después del trabajo asincrónico.Yo uso mucho este enfoque. Me interesaría saber qué tan bien funciona este enfoque donde se involucra el cableado del resultado a través de módulos consecutivos.
fuente
result
. Funciona porque está leyendo la variable después de que se completa la función asíncrona.Si bien las promesas y las devoluciones de llamada funcionan bien en muchas situaciones, es difícil expresar algo como:
Terminarías atravesando
async1
; compruebe siname
está indefinido o no y llame a la devolución de llamada en consecuencia.Si bien está bien en pequeños ejemplos, se vuelve molesto cuando tiene muchos casos similares y manejo de errores involucrado.
Fibers
Ayuda a resolver el problema.Puedes ver el proyecto aquí .
fuente
async-await
si está utilizando algunas de las versiones más recientes de node. Si alguien está atascado con versiones anteriores, puede usar este método.El siguiente ejemplo que he escrito muestra cómo
Este ejemplo de trabajo es autónomo. Definirá un objeto de solicitud simple que usa el
XMLHttpRequest
objeto de ventana para realizar llamadas. Definirá una función simple para esperar a que se completen un montón de promesas.Contexto. El ejemplo es consultar el punto final de la API web de Spotify para buscar
playlist
objetos para un conjunto dado de cadenas de consulta:Para cada elemento, una nueva Promesa disparará un bloque -
ExecutionBlock
, analizará el resultado, programará un nuevo conjunto de promesas en función de la matriz de resultados, es decir, una lista deuser
objetos de Spotify y ejecutará la nueva llamada HTTP dentro delExecutionProfileBlock
asíncrono.Luego puede ver una estructura de Promesa anidada, que le permite generar múltiples llamadas HTTP anidadas completamente asíncronas, y unir los resultados de cada subconjunto de llamadas
Promise.all
.NOTA Las
search
API de Spotify recientes requerirán que se especifique un token de acceso en los encabezados de solicitud:Entonces, para ejecutar el siguiente ejemplo, debe colocar su token de acceso en los encabezados de solicitud:
He discutido ampliamente esta solución aquí .
fuente
La respuesta corta es que debe implementar una devolución de llamada como esta:
fuente
Respuesta de 2017: ahora puede hacer exactamente lo que quiere en cada navegador y nodo actual
Esto es bastante simple:
Aquí hay una versión funcional de su código:
esperar es compatible con todos los navegadores actuales y el nodo 8
fuente
await/async
El navegador se puede dividir en tres partes:
1) Bucle de eventos
2) API web
3) Cola de eventos
Event Loop se ejecuta para siempre, es decir, un tipo de bucle infinito. Event Queue es donde todas sus funciones se insertan en algún evento (ejemplo: clic), esto se realiza una por una fuera de la cola y se coloca en Event loop que ejecuta esta función y la prepara para el siguiente después de que se ejecute el primero. Esto significa que la ejecución de una función no comienza hasta que la función anterior a la cola se ejecute en el bucle de eventos.
Ahora pensemos que empujamos dos funciones en una cola, una es para obtener datos del servidor y otra utiliza esos datos. Primero empujamos la función serverRequest () en la cola y luego utilizamos la función Data (). La función serverRequest entra en el bucle de eventos y realiza una llamada al servidor, ya que nunca sabemos cuánto tiempo llevará obtener datos del servidor, por lo que se espera que este proceso tome tiempo y, por lo tanto, ocupamos nuestro bucle de eventos, colgando así nuestra página, ahí es donde Web API entra en función, toma esta función del bucle de eventos y trata con el servidor haciendo que el bucle de eventos sea gratuito para que podamos ejecutar la siguiente función desde la cola. La siguiente función en la cola es utiliseData () que va en bucle pero debido a que no hay datos disponibles, se va el desperdicio y la ejecución de la siguiente función continúa hasta el final de la cola (esto se llama llamada asíncrona, es decir, podemos hacer algo más hasta que obtengamos datos)
Supongamos que nuestra función serverRequest () tenía una declaración de retorno en un código, cuando recuperamos los datos de la API web del servidor la empujará a la cola al final de la cola. A medida que se empuja al final de la cola, no podemos utilizar sus datos, ya que no queda ninguna función en nuestra cola para utilizar estos datos. Por lo tanto, no es posible devolver algo de Async Call.
Por lo tanto, la solución a esto es la devolución de llamada o la promesa .
Una imagen de una de las respuestas aquí, explica correctamente el uso de la devolución de llamada ... Le damos a nuestra función (función que utiliza los datos devueltos por el servidor) a la función que llama al servidor.
En mi código se llama como
Devolución de llamada Javscript.info
fuente
Puede usar esta biblioteca personalizada (escrita usando Promise) para hacer una llamada remota.
Ejemplo de uso simple:
fuente
Otra solución es ejecutar código a través del ejecutor secuencial nsynjs .
Si se promete la función subyacente
nsynjs evaluará todas las promesas secuencialmente y colocará el resultado de la promesa en
data
propiedad:Si no se promete la función subyacente
Paso 1. Ajuste la función con devolución de llamada en un contenedor compatible con nsynjs (si tiene una versión prometida, puede omitir este paso):
Paso 2. Ponga en funcionamiento la lógica síncrona:
Paso 3. Ejecuta la función de manera síncrona a través de nsynjs:
Nsynjs evaluará todos los operadores y expresiones paso a paso, pausando la ejecución en caso de que el resultado de alguna función lenta no esté lista.
Más ejemplos aquí: https://github.com/amaksr/nsynjs/tree/master/examples
fuente
ECMAScript 6 tiene 'generadores' que le permiten programar fácilmente en un estilo asincrónico.
Para ejecutar el código anterior, haga esto:
Si necesita apuntar a navegadores que no admiten ES6, puede ejecutar el código a través de Babel o el compilador de cierre para generar ECMAScript 5.
Las devoluciones de llamada
...args
se envuelven en una matriz y se desestructuran cuando las lee para que el patrón pueda hacer frente a devoluciones de llamada que tienen múltiples argumentos. Por ejemplo con el nodo fs :fuente
Aquí hay algunos enfoques para trabajar con solicitudes asincrónicas:
Ejemplo: implementación diferida de jQuery para trabajar con múltiples solicitudes
fuente
Nos encontramos en un universo que parece progresar a lo largo de una dimensión que llamamos "tiempo". Realmente no entendemos qué hora es, pero hemos desarrollado abstracciones y vocabulario que nos permiten razonar y hablar sobre ello: "pasado", "presente", "futuro", "antes", "después".
Los sistemas informáticos que construimos, cada vez más, tienen el tiempo como una dimensión importante. Ciertas cosas están preparadas para suceder en el futuro. Luego, otras cosas deben suceder después de que esas primeras cosas eventualmente ocurran. Esta es la noción básica llamada "asincronía". En nuestro mundo cada vez más conectado en red, el caso más común de asincronía es esperar que algún sistema remoto responda a alguna solicitud.
Considera un ejemplo. Llamas al lechero y pides un poco de leche. Cuando llegue, querrás ponerlo en tu café. No puedes poner la leche en tu café en este momento, porque todavía no está aquí. Tienes que esperar a que llegue antes de ponerlo en tu café. En otras palabras, lo siguiente no funcionará:
Debido a que JS no tiene forma de saber que necesita esperar a
order_milk
que termine antes de ejecutarseput_in_coffee
. En otras palabras, no sabe queorder_milk
es asíncrono, es algo que no va a producir leche hasta algún momento futuro. JS y otros lenguajes declarativos ejecutan una declaración tras otra sin esperar.El enfoque clásico de JS para este problema, aprovechando el hecho de que JS admite funciones como objetos de primera clase que se pueden pasar, es pasar una función como parámetro a la solicitud asincrónica, que luego invocará cuando se haya completado su tarea en algún momento en el futuro. Ese es el enfoque de "devolución de llamada". Se parece a esto:
order_milk
comienza, ordena la leche, luego, cuando y solo cuando llega, invocaput_in_coffee
.El problema con este enfoque de devolución de llamada es que contamina la semántica normal de una función que informa su resultado
return
; en cambio, las funciones no deben informar sus resultados llamando a una devolución de llamada dada como un parámetro. Además, este enfoque puede volverse difícil de manejar rápidamente cuando se trata de secuencias de eventos más largas. Por ejemplo, digamos que quiero esperar a que se ponga la leche en el café y luego, y solo entonces, realizar un tercer paso, es decir, beber el café. Termino necesitando escribir algo como esto:donde estoy pasando
put_in_coffee
tanto la leche para ponerla como la acción (drink_coffee
) para ejecutar una vez que se ha puesto la leche. Tal código se vuelve difícil de escribir, leer y depurar.En este caso, podríamos reescribir el código en la pregunta como:
Introduce promesas
Esta fue la motivación para la noción de una "promesa", que es un tipo particular de valor que representa un resultado futuro o asíncrono de algún tipo. Puede representar algo que ya sucedió, o que va a suceder en el futuro, o puede que nunca suceda. Las promesas tienen un único método, llamado
then
, al que pasa una acción para que se ejecute cuando el resultado que representa la promesa se ha cumplido.En el caso de nuestra leche y café, diseñamos
order_milk
devolver una promesa para la llegada de la leche, luego especificamosput_in_coffee
comothen
acción, de la siguiente manera:Una ventaja de esto es que podemos unirlos para crear secuencias de sucesos futuros ("encadenamiento"):
Apliquemos promesas a su problema particular. Envolveremos nuestra lógica de solicitud dentro de una función, que devuelve una promesa:
En realidad, todo lo que hemos hecho es agregar
return
a la llamada a$.ajax
. Esto funciona porque jQuery$.ajax
ya devuelve una especie de promesa. (En la práctica, sin entrar en detalles, preferiríamos finalizar esta llamada para devolver una promesa real, o usar alguna alternativa a$.ajax
eso). Ahora, si queremos cargar el archivo y esperar a que termine y entonces hacer algo, simplemente podemos decirpor ejemplo,
Cuando usamos promesas, terminamos pasando muchas funciones
then
, por lo que a menudo es útil usar las funciones de flecha de estilo ES6 más compactas:La
async
palabra clavePero todavía hay algo vagamente insatisfactorio sobre tener que escribir código de una manera si es sincrónica y de una manera bastante diferente si es asíncrona. Para síncrono, escribimos
pero si
a
es asíncrono, con promesas tenemos que escribirArriba, dijimos, "JS no tiene forma de saber que necesita esperar a que termine la primera llamada antes de ejecutar la segunda". ¿No sería agradable si era alguna forma de saber que JS? Resulta que la hay, la
await
palabra clave, utilizada dentro de un tipo especial de función llamada función "asíncrona". Esta característica es parte de la próxima versión de ES pero ya está disponible en transpiladores como Babel con los ajustes preestablecidos correctos. Esto nos permite simplemente escribirEn su caso, podría escribir algo como
fuente
Respuesta corta : su
foo()
método regresa inmediatamente, mientras que la$ajax()
llamada se ejecuta de forma asíncrona después de que la función regresa . El problema es entonces cómo o dónde almacenar los resultados recuperados por la llamada asíncrona una vez que regresa.Se han dado varias soluciones en este hilo. Quizás la forma más fácil es pasar un objeto al
foo()
método y almacenar los resultados en un miembro de ese objeto después de que se complete la llamada asíncrona.Tenga en cuenta que la llamada a
foo()
aún no devolverá nada útil. Sin embargo, el resultado de la llamada asincrónica ahora se almacenará enresult.response
.fuente
Use una
callback()
función dentro delfoo()
éxito. Intenta de esta manera. Es simple y fácil de entender.fuente
La pregunta fue:
que PUEDE interpretarse como:
La solución será evitar devoluciones de llamada y usar una combinación de Promesas y asíncrono / espera .
Me gustaría dar un ejemplo para una solicitud de Ajax.
(Aunque puede escribirse en Javascript, prefiero escribirlo en Python y compilarlo en Javascript usando Transcrypt . Será lo suficientemente claro).
Primero habilitemos el uso de JQuery, para tener
$
disponible comoS
:Defina una función que devuelva una Promesa , en este caso una llamada Ajax:
Use el código asincrónico como si fuera síncrono :
fuente
Usando Promesa
La respuesta más perfecta a esta pregunta es usar
Promise
.Uso
Pero espera...!
¡Hay un problema con el uso de promesas!
¿Por qué deberíamos usar nuestra propia promesa personalizada?
Estuve usando esta solución por un tiempo hasta que descubrí que hay un error en los navegadores antiguos:
Así que decidí implementar mi propia clase Promise para ES3 a los compiladores js a continuación si no está definido. Simplemente agregue este código antes de su código principal y luego use Promise con seguridad.
fuente
Por supuesto, hay muchos enfoques, como solicitud síncrona, promesa, pero desde mi experiencia creo que debería usar el enfoque de devolución de llamada. Es natural el comportamiento asincrónico de Javascript. Por lo tanto, su fragmento de código puede reescribirse un poco diferente:
fuente
En lugar de arrojarle código, hay 2 conceptos que son clave para comprender cómo JS maneja las devoluciones de llamadas y la asincronía. (¿es eso una palabra?)
El modelo de bucle de eventos y concurrencia
Hay tres cosas que debe tener en cuenta; La cola; el bucle de eventos y la pila
En términos amplios y simplistas, el bucle de eventos es como el administrador del proyecto, está constantemente escuchando cualquier función que quiera ejecutarse y se comunica entre la cola y la pila.
Una vez que recibe un mensaje para ejecutar algo, lo agrega a la cola. La cola es la lista de cosas que están esperando para ejecutarse (como su solicitud AJAX). imagínalo así:
Cuando uno de estos mensajes se va a ejecutar, saca el mensaje de la cola y crea una pila, la pila es todo lo que JS necesita ejecutar para realizar la instrucción en el mensaje. Entonces en nuestro ejemplo se le dice que llame
foobarFunc
Entonces, cualquier cosa que foobarFunc necesite ejecutar (en nuestro caso
anotherFunction
) será empujada a la pila. ejecutado y luego olvidado: el bucle de eventos pasará al siguiente elemento de la cola (o escuchará mensajes)La clave aquí es el orden de ejecución. Es decir
CUANDO va a correr algo
Cuando realiza una llamada con AJAX a una parte externa o ejecuta un código asincrónico (un setTimeout, por ejemplo), Javascript depende de una respuesta antes de que pueda continuar.
La gran pregunta es ¿cuándo obtendrá la respuesta? La respuesta es que no lo sabemos, por lo que el bucle de eventos está esperando que ese mensaje diga "oye, corre conmigo". Si JS solo esperara ese mensaje sincrónicamente, su aplicación se congelaría y apestaría. Entonces JS continúa ejecutando el siguiente elemento en la cola mientras espera que el mensaje se agregue nuevamente a la cola.
Es por eso que con la funcionalidad asincrónica usamos cosas llamadas devoluciones de llamada . Es como una promesa literalmente. Como en Prometo devolver algo en algún momento, jQuery usa devoluciones de llamada específicas llamadas
deffered.done
deffered.fail
ydeffered.always
(entre otras). Puedes verlos todos aquíEntonces, lo que debe hacer es pasar una función que se promete ejecutar en algún momento con los datos que se le pasan.
Debido a que una devolución de llamada no se ejecuta de inmediato, pero más adelante es importante pasar la referencia a la función que no se ejecutó. entonces
así que la mayoría de las veces (pero no siempre), pasará por
foo
nofoo()
Esperemos que tenga sentido. Cuando encuentre cosas como esta que parecen confusas, le recomiendo leer la documentación completa para al menos comprenderla. Te convertirá en un desarrollador mucho mejor.
fuente
Usando ES2017 debería tener esto como la declaración de función
Y ejecutándolo así.
O la sintaxis de Promise
fuente