¿Cómo devuelvo la respuesta de una llamada asíncrona?

Tengo una función foo que hace una solicitud Ajax. ¿Cómo puedo devolver la respuesta de foo ?

Intenté devolver el valor de la callback correcta, así como asignar la respuesta a una variable local dentro de la función y devolverla, pero ninguna de esas formas devuelve la respuesta.

 function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // return response; // <- I tried that one as well } }); return result; } var result = foo(); // It always ends up being `undefined`. 

→ Para obtener una explicación más general del comportamiento asíncrono con diferentes ejemplos, consulte ¿Por qué no se modifica mi variable después de modificarla dentro de una función? – Referencia de código asíncrono.

→ Si ya entiende el problema, vaya a las posibles soluciones a continuación.

El problema

La A en Ajax significa asíncrono . Eso significa que el envío de la solicitud (o más bien la recepción de la respuesta) se elimina del flujo de ejecución normal. En su ejemplo, $.ajax devuelve inmediatamente y la siguiente statement, return result; , se ejecuta antes de que la función que pasó como llamada de callback success se haya ejecutado.

Aquí hay una analogía que, con suerte, hace que la diferencia entre flujo síncrono y asíncrono sea más clara:

Sincrónico

Imagina que haces una llamada telefónica a un amigo y le pides que te busque algo. Aunque puede tardar un rato, esperas en el teléfono y miras al espacio, hasta que tu amigo te da la respuesta que necesitabas.

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(); 

A pesar de que findItem puede tardar mucho tiempo en ejecutarse, cualquier código que venga después de var item = findItem(); Tiene que esperar hasta que la función devuelva el resultado.

Asincrónico

Llamas a tu amigo otra vez por la misma razón. Pero esta vez le dices que tienes prisa y que debería devolverte la llamada desde tu teléfono móvil. Cuelgas, sales de casa y haces lo que planeas hacer. Una vez que tu amigo te devuelve la llamada, estás tratando con la información que te dio.

Eso es exactamente lo que está sucediendo 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 de inmediato y se ejecuta la instrucción después de la llamada Ajax. Para obtener la respuesta eventualmente, proporciona una función para que se llame una vez que se recibió la respuesta, una callback (¿nota algo? ¿ Devuelve una llamada ?). Cualquier statement que venga después de esa llamada se ejecuta antes de que se llame la callback.


Solución (s)

¡Abraza la naturaleza asíncrona de JavaScript! Si bien ciertas operaciones asíncronas proporcionan contrapartes síncronas (también lo hace “Ajax”), generalmente se desaconseja su uso, especialmente en el contexto de un navegador.

¿Por qué es malo lo preguntas?

JavaScript se ejecuta en el subproceso de la interfaz de usuario del navegador y cualquier proceso de larga ejecución bloqueará la interfaz de usuario, lo que dejará de responder. Además, hay un límite superior en el tiempo de ejecución para JavaScript y el navegador le preguntará al usuario si desea continuar la ejecución o no.

Todo esto es realmente una mala experiencia de usuario. El usuario no podrá decir si todo está funcionando 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 construyen una encima de la otra:

  • Promesas con async/await await (ES2017 +, disponible en navegadores más antiguos si utiliza un transpiler o regenerador)
  • Callbacks (popular en el nodo)
  • Promesas con then() (ES2015 +, disponible en navegadores más antiguos si utiliza una de las muchas bibliotecas de promesa)

Los tres están disponibles en los navegadores actuales, y el nodo 7+.


ES2017 +: Promesas con async/await

La versión ECMAScript lanzada en 2017 introdujo el soporte de nivel de syntax para funciones asíncronas. Con la ayuda de async y await , puede escribir asincrónico en un “estilo síncrono”. El código sigue siendo asíncrono, pero es más fácil de leer / entender.

async/await basa en promesas: una función async siempre devuelve una promesa. await “deshacer” una promesa y resulte 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 función async . Eso significa que en el nivel más alto, todavía tienes que trabajar directamente con la promesa.

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; } } // Async functions always return a promise getAllBooks() .then(function(books) { console.log(books); }); 

Las versiones actuales de navegador y nodo admiten async/await . También puede admitir entornos más antiguos transformando su código a ES5 con la ayuda de un regenerador (o herramientas que usan un regenerador, como Babel ).


Deje que las funciones acepten devoluciones de llamada

Una callback 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 asíncrono, la callback se llamará siempre que se realice el proceso asíncrono. Por lo general, el resultado se pasa a la callback.

En el ejemplo de la pregunta, puede hacer que foo acepte una callback y la utilice como callback success . Así que esto

 var result = foo(); // Code that depends on 'result' 

se convierte en

 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 sí se define de la siguiente manera:

 function foo(callback) { $.ajax({ // ... success: callback }); } 

callback se referirá a la función que pasamos a foo cuando la llamamos y simplemente la pasamos al success . Es decir, una vez que la solicitud de Ajax sea exitosa, $.ajax llamará la callback y pasará la respuesta a la callback (a la que se puede hacer referencia con el result , ya que así es como definimos la callback).

También puede procesar la respuesta antes de pasarla a la callback:

 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 está fuertemente controlado por eventos (eventos DOM). Recibir la respuesta Ajax no es más que un evento.
Podrían surgir dificultades cuando tenga que trabajar con un código de terceros, pero la mayoría de los problemas se pueden resolver simplemente pensando en el flujo de la aplicación.


ES2015 +: Promesas con entonces ()

La API de Promise es una nueva característica de ECMAScript 6 (ES2015), pero ya tiene un buen soporte de navegador . 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 las funciones asíncronas (por ejemplo, bluebird ).

Las promesas son contenedores de 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 redactar.

Aquí hay un ejemplo simple de usar 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 ofrece la promesa va más allá del scope de esta respuesta, pero si escribe un nuevo código, debe considerarlos 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: objetos diferidos de jQuery.

Los objetos diferidos son la implementación personalizada de promesas de jQuery (antes de que la API de Promise fuera estandarizada). Se comportan casi como promesas, pero exponen una API ligeramente diferente.

Cada método Ajax de jQuery ya devuelve un “objeto diferido” (en realidad una promesa de un objeto diferido) que puede devolver desde su función:

 function ajax() { return $.ajax(...); } ajax().done(function(result) { // Code depending on result }).fail(function() { // An error occurred }); 

Nota al margen: Promesa gotchas

Tenga en cuenta que las promesas y los objetos diferidos son solo contenedores para un valor futuro, no son el valor en sí mismo. Por ejemplo, supongamos que tienes 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 malinterpreta los problemas de asincronía anteriores. Específicamente, $.ajax() no congela el código mientras comprueba la página ‘/ password’ en su servidor: envía una solicitud al servidor y, mientras espera, devuelve inmediatamente un objeto AQax diferido de jQuery, no la respuesta de el servidor. Eso significa que la instrucción if 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 asíncronas tienen contrapartes síncronas. No abogo por su uso, pero en aras de la integridad, aquí es cómo realizaría una llamada sincrónica:

Sin jQuery

Si usa directamente un objeto XMLHTTPRequest , pase false como tercer argumento a .open .

jQuery

Si usa jQuery , puede establecer la opción async en false . Tenga en cuenta que esta opción está en desuso desde jQuery 1.8. Entonces puede seguir usando una callback success o acceder a la propiedad responseText 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 los parámetros de configuración a $.ajax ).

¡Aviso! No es posible realizar una solicitud JSONP síncrona. JSONP por su propia naturaleza siempre es asíncrono (una razón más para no considerar esta opción).

Si no estás usando jQuery en tu código, esta respuesta es para ti.

Su código debe ser algo como esto:

 function foo() { var httpRequest = new XMLHttpRequest(); httpRequest.open('GET', "/echo/json"); httpRequest.send(); return httpRequest.responseText; } var result = foo(); // always ends up being 'undefined' 

Felix Kling hizo un buen trabajo al escribir una respuesta para las personas que usan jQuery para AJAX. Decidí ofrecer una alternativa para las personas que no lo son.

( Tenga en cuenta que para aquellos que utilizan la nueva API de fetch , Angular o promesas, he agregado otra respuesta a continuación )


A lo que te enfrentas

Este es un breve resumen de la “Explicación del problema” de la otra respuesta, si no está seguro después de leer esto, lea esto.

La A en AJAX significa asíncrono . Eso significa que el envío de la solicitud (o más bien la recepción de la respuesta) se elimina del flujo de ejecución normal. En su ejemplo, .send devuelve inmediatamente y la siguiente statement return result; , se ejecuta antes de que la función que pasó como llamada de callback success se haya ejecutado.

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 simple analogía.

 function getFive(){ var a; setTimeout(function(){ a=5; },10); return a; } 

(Violín)

El valor de a valor devuelto undefined está undefined ya que la parte a=5 aún no se ha ejecutado. AJAX actúa de esta manera: 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 activa , indicando a su progtwig qué hacer cuando se complete el cálculo.

 function onComplete(a){ // When the code completes, do this alert(a); } function getFive(whenDone){ var a; setTimeout(function(){ a=5; whenDone(a); },10); } 

Esto se llama CPS . Básicamente, pasamos a getFive una acción para realizar cuando se completa, le decimos a nuestro código cómo reactjsr cuando se completa un evento (como nuestra llamada AJAX, o en este caso, el tiempo de espera).

El uso sería:

 getFive(onComplete); 

Lo que debería alertar “5” a la pantalla. (Violín) .

Soluciones posibles

Básicamente hay dos maneras de resolver esto:

  1. Haga que la llamada AJAX sea sincrónica (llamémosla SJAX).
  2. Reestructura tu código para que funcione correctamente con devoluciones de llamada.

1. AJAX síncrono – ¡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é:

XMLHttpRequest admite comunicaciones síncronas y asíncronas. En general, sin embargo, las solicitudes asíncronas deberían preferirse a las solicitudes sincrónicas por razones de rendimiento.

En resumen, las solicitudes síncronas bloquean la ejecución del código … … esto puede causar problemas graves …

Si tienes que hacerlo, puedes pasar una bandera: Aquí es cómo:

 var request = new XMLHttpRequest(); request.open('GET', 'yourURL', false); // `false` makes the request synchronous request.send(null); if (request.status === 200) {// That's HTTP for 'ok' console.log(request.responseText); } 

2. Reestructurar el código.

Deja que tu función acepte una callback. En el código de ejemplo, se puede hacer que foo acepte una callback. Le diremos a nuestro código cómo reactjsr cuando foo complete.

Asi que:

 var result = foo(); // code that depends on `result` goes here 

Se convierte en

 foo(function(result) { // code that depends on `result` }); 

Aquí pasamos una función anónima, pero también podríamos pasar una referencia a una función existente, haciendo que parezca:

 function myHandler(result) { // code that depends on `result` } foo(myHandler); 

Para obtener más detalles sobre cómo se hace este tipo de diseño de callback, verifique la respuesta de Felix.

Ahora, definamos foo para actuar en consecuencia.

 function foo(callback) { var httpRequest = new XMLHttpRequest(); httpRequest.onload = function(){ // when the request is loaded callback(httpRequest.responseText);// we're calling our method }; httpRequest.open('GET', "/echo/json"); httpRequest.send(); } 

(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 aún más verificando si el estado de la respuesta no es 200 y actuando en consecuencia (crear un controlador de fallas y tal). Resolviendo efectivamente nuestro problema.

Si aún tiene dificultades para entender esto, lea la guía de inicio de AJAX en MDN.

XMLHttpRequest 2 (en primer lugar, lea las respuestas de Benjamin Gruenbaum y Felix Kling)

Si no usa jQuery y desea un buen XMLHttpRequest 2 corto que funcione en los navegadores modernos y también en los navegadores móviles, sugiero usarlo de esta manera:

 function ajax(a, b, c){ // URL, callback, just a placeholder c = new XMLHttpRequest; c.open('GET', a); c.onload = b; c.send() } 

Como puedes ver:

  1. Es más corto que todas las demás funciones enumeradas.
  2. La callback se establece directamente (por lo que no hay cierres innecesarios adicionales).
  3. Utiliza la nueva carga (para que no tenga que verificar el estado de & estado de readystate)
  4. Hay algunas otras situaciones que no recuerdo que hacen que el XMLHttpRequest 1 sea molesto.

Hay dos formas de obtener la respuesta de esta llamada Ajax (tres usando el nombre var de XMLHttpRequest):

Lo más simple:

 this.response 

O si por alguna razón usted bind() la callback a una clase:

 e.target.response 

Ejemplo:

 function callback(e){ console.log(this.response); } ajax('URL', callback); 

O (la anterior es mejor las funciones anónimas son siempre un problema):

 ajax('URL', function(e){console.log(this.response)}); 

Nada mas facil

Ahora, algunas personas probablemente dirán que es mejor usar onreadystatechange o incluso el nombre de la variable XMLHttpRequest. Eso está mal.

Echa un vistazo a las características avanzadas de XMLHttpRequest

Es compatible con 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 nombre de la variable XMLHttpRequest es otro gran error, ya que necesita ejecutar la callback dentro de los cierres de onload / oreadystatange o si no lo perdió.


Ahora, si quieres algo más complejo usando Post y FormData, puedes extender esta función fácilmente:

 function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.send(d||null) } 

Nuevamente … es una función muy corta, pero se obtiene y se publica.

Ejemplos de uso:

 x(url, callback); // By default it's get so no need to set x(url, callback, 'post', {'key': 'val'}); // No need to set post data 

O pase un elemento de formulario completo ( document.getElementsByTagName('form')[0] ):

 var fd = new FormData(form); x(url, callback, 'post', fd); 

O establece algunos valores personalizados:

 var fd = new FormData(); fd.append('key', 'val') x(url, callback, 'post', fd); 

Como pueden ver, no implementé la sincronización … es algo malo.

Habiendo dicho eso … ¿por qué no hacerlo de la manera más fácil?


Como se mencionó en el comentario, el uso de error && sincrónico rompe completamente el punto de la respuesta. ¿Cuál es una buena forma corta de usar Ajax de la manera correcta?

Manejador de errores

 function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder c = new XMLHttpRequest; c.open(e||'get', a); c.onload = b; c.onerror = error; c.send(d||null) } function error(e){ console.log('--Error--', this.type); console.log('this: ', this); console.log('Event: ', e) } function displayAjax(e){ console.log(e, this); } x('WRONGURL', displayAjax); 

En la secuencia de comandos anterior, tiene un controlador de errores que está definido estáticamente para que no comprometa la función. El controlador de errores también se puede utilizar para otras funciones.

Pero para realmente eliminar un error, la única forma es escribir una URL incorrecta, en cuyo caso cada navegador arroja un error.

Los manejadores de errores pueden ser útiles si establece encabezados personalizados, establece responseType en blob array buffer o lo que sea …

Incluso si pasas ‘POSTAPAPAP’ como método no arrojará un error.

Incluso si pasas ‘fdggdgilfdghfldj’ como formdata no generará ningún error.

En el primer caso, el error está dentro de displayAjax() bajo this.statusText como Method not Allowed .

En el segundo caso, simplemente funciona. Debes verificar en el lado del servidor si pasaste los datos de publicación correctos.

dominio cruzado no permitido lanza 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 manejador 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 callback 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 total … ??? Así que revisé 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 lol’d


Leer un archivo síncrono

No hagas eso

Si desea bloquear el navegador por un tiempo, cargue un buen archivo txt grande sincrónico.

 function omg(a, c){ // URL c = new XMLHttpRequest; c.open('GET', a, true); c.send(); return c; // Or c.response } 

Ahora puedes hacer

  var res = omg('thisIsGonnaBlockThePage.txt'); 

No hay otra forma de hacer esto de forma no asíncrona. (Sí, con setTimeout loop … pero en serio?)

Otro punto es … si trabaja con API o simplemente posee los archivos de la lista o lo que sea, siempre usa diferentes funciones para cada solicitud …

Solo si tiene una página en la que carga siempre el mismo XML / JSON o lo que necesite, solo 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 quieres AMPLIAR 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, con solo GET …

Pero puedes hacer muchas cosas con XMLHttpRequest 2:

Hice un administrador de descargas (usando rangos en ambos lados con curriculum vitae, lector de archivos, sistema de archivos), varios convertidores de resizadores de imágenes usando canvass, rellenando bases de datos websql con base64images y mucho más … Pero en estos casos, debe crear una función solo para ese propósito … a veces necesitas un blob, búferes de matriz, puedes establecer 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.)

Si estás usando promesas, esta respuesta es para ti.

Esto significa AngularJS, jQuery (con diferido), reemplazo nativo de XHR (fetch), EmberJS, guardado de BackboneJS o cualquier biblioteca de nodos que devuelva promesas.

Su código debe ser algo como esto:

 function foo() { var data; // or $.get(...).then, or request(...).then, or query(...).then fetch("/echo/json").then(function(response){ data = response.json(); }); return data; } var result = foo(); // result is always undefined no matter what. 

Felix Kling hizo un buen trabajo al escribir 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 promesas en el frontend o en el backend.


El tema 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 controladores then se ejecutan de forma asíncrona, es decir, después del código debajo de ellos que no está en un controlador .then .

Esto significa que cuando está devolviendo data el controlador then que ha definido no se ejecutó todavía. A su vez, esto significa que el valor que está devolviendo no se ha establecido en el valor correcto en el tiempo.

Aquí hay una analogía simple para el problema:

  function getFive(){ var data; setTimeout(function(){ // set a timer for one second in the future data = 5; // after a second, do this }, 1000); return data; } document.body.innerHTML = getFive(); // `undefined` here and not 5 

You are using Ajax incorrectly. The idea is not to have it return anything, but instead hand off the data to something called a callback function, which handles the data.

Es decir:

 function handleData( responseData ) { // Do what you want with the data console.log(responseData); } $.ajax({ url: "hi.php", ... success: function ( data, status, XHR ) { handleData(data); } }); 

Returning anything in the submit handler will not do anything. You must instead either hand off the data, or do what you want with it directly inside the success function.

The simplest solution is create a JavaScript function and call it for the Ajax success callback.

 function callServerAsync(){ $.ajax({ url: '...', success: function(response) { successCallback(response); } }); } function successCallback(responseObj){ // Do something like read the response and show data alert(JSON.stringify(responseObj)); // Only applicable to JSON response } function foo(callback) { $.ajax({ url: '...', success: function(response) { return callback(null, response); } }); } var result = foo(function(err, result){ if (!err) console.log(result); }); 

I will answer with a horrible-looking, hand-drawn comic. The second image is the reason why result is undefined in your code example.

introduzca la descripción de la imagen aquí

Angular1

For people who are using AngularJS , can handle this situation using Promises .

Here it says,

Promises can be used to unnest asynchronous functions and allows one to chain multiple functions together.

You can find a nice explanation here also.

Example found in docs mentioned below.

  promiseB = promiseA.then( function onSuccess(result) { return result + 1; } ,function onError(err) { //Handle error } ); // promiseB will be resolved immediately after promiseA is resolved // and its value will be the result of promiseA incremented by 1. 

Angular2 and Later

In Angular2 with look at the following example, but its recommended to use Observables with Angular2 .

  search(term: string) { return this.http .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`) .map((response) => response.json()) .toPromise(); 

}

You can consume that in this way,

 search() { this.searchService.search(this.searchField.value) .then((result) => { this.result = result.artists.items; }) .catch((error) => console.error(error)); } 

See the original post here. But Typescript does not support native es6 Promises , if you want to use it, you might need plugin for that.

Additionally here is the promises spec define here.

Most of the answers here give useful suggestions for when you have a single async operation, but sometimes, this comes up when you need to do an asynchronous operation for each entry in an array or other list-like structure. The temptation is to do this:

 // WRONG var results = []; theArray.forEach(function(entry) { doSomethingAsync(entry, function(result) { results.push(result); }); }); console.log(results); // Eg, using them, returning them, etc. 

Ejemplo:

 // WRONG var theArray = [1, 2, 3]; var results = []; theArray.forEach(function(entry) { doSomethingAsync(entry, function(result) { results.push(result); }); }); console.log("Results:", results); // Eg, using them, returning them, etc. function doSomethingAsync(value, callback) { console.log("Starting async operation for " + value); setTimeout(function() { console.log("Completing async operation for " + value); callback(value * 2); }, Math.floor(Math.random() * 200)); } 
 .as-console-wrapper { max-height: 100% !important; } 

Have a look at this example:

 var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope,$http) { var getJoke = function(){ return $http.get('http://api.icndb.com/jokes/random').then(function(res){ return res.data.value; }); } getJoke().then(function(res) { console.log(res.joke); }); }); 

As you can see getJoke is returning a resolved promise (it is resolved when returning res.data.value ). So you wait until the $http.get request is completed and then console.log(res.joke) is executed (as a normal asynchronous flow).

This is the plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

Another approach to return a value from an asynchronous function, is to pass in an object that will store the result from the asynchronous function.

Here is an example of the same:

 var async = require("async"); // This wires up result back to the caller var result = {}; var asyncTasks = []; asyncTasks.push(function(_callback){ // some asynchronous operation $.ajax({ url: '...', success: function(response) { result.response = response; _callback(); } }); }); async.parallel(asyncTasks, function(){ // result is available after performing asynchronous operation console.log(result) console.log('Done'); }); 

I am using the result object to store the value during the asynchronous operation. This allows the result be available even after the asynchronous job.

I use this approach a lot. I would be interested to know how well this approach works where wiring the result back through consecutive modules is involved.

This is one of the places which two ways data binding that’s used in many new JavaScript frameworks will work greatly for you…

So if you are using Angular, React or any other frameworks which do two ways data binding, this issue is simply fixed for you, so in easy word, your result is undefined at the first stage, so you have got result = undefined before you receive the data, then as soon as you get the result, it will updated and get assigned to the new value which is respond of your Ajax call…

But how you can do it in pure javascript or jQuery for example as you asked in this question?

You can use a callback , promise and recently observable to handle it for you, for example in promises we have some function like success() or then() which will be executed when your data is ready for you, same with callback or subscribe function on observable .

For example in your case which you are using jQuery , you can do something like this:

 $(document).ready(function(){ function foo() { $.ajax({url: "api/data", success: function(data){ fooDone(data); //after we have data, we pass it to fooDone }}); }; function fooDone(data) { console.log(data); //fooDone has the data and console.log it }; foo(); //call happens here }); 

For more information study about promises and observables which are newer ways to do this async stuffs.

While promises and callbacks work fine in many situations, it is a pain in the rear to express something like:

 if (!name) { name = async1(); } async2(name); 

You’d end up going through async1 ; check if name is undefined or not and call the callback accordingly.

 async1(name, callback) { if (name) callback(name) else { doSomething(callback) } } async1(name, async2) 

While it is okay in small examples it gets annoying when you have a lot of similar cases and error handling involved.

Fibers helps in solving the issue.

 var Fiber = require('fibers') function async1(container) { var current = Fiber.current var result doSomething(function(name) { result = name fiber.run() }) Fiber.yield() return result } Fiber(function() { var name if (!name) { name = async1() } async2(name) // Make any number of async calls from here } 

You can checkout the project here .

Short answer is, you have to implement a callback like this:

 function callback(response) { // Here you can do what ever you want with the response object. console.log(response); } $.ajax({ url: "...", success: callback }); 

The following example I have written shows how to

  • Handle asynchronous HTTP calls;
  • Wait for response from each API call;
  • Use Promise pattern;
  • Use Promise.All pattern to join multiple HTTP calls;

This working example is self-contained. It will define a simple request object that uses the window XMLHttpRequest object to make calls. It will define a simple function to wait for a bunch of promises to be completed.

Contexto. The example is querying the Spotify Web API endpoint in order to search for playlist objects for a given set of query strings:

 [ "search?type=playlist&q=%22doom%20metal%22", "search?type=playlist&q=Adele" ] 

For each item, a new Promise will fire a block – ExecutionBlock , parse the result, schedule a new set of promises based on the result array, that is a list of Spotify user objects and execute the new HTTP call within the ExecutionProfileBlock asynchronously.

You can then see a nested Promise structure, that lets you spawn multiple and completely asynchronous nested HTTP calls, and join the results from each subset of calls through Promise.all .

NOTE Recent Spotify search APIs will require an access token to be specified in the request headers:

 -H "Authorization: Bearer {your access token}" 

So, you to run the following example you need to put your access token in the request headers:

 var spotifyAccessToken = "YourSpotifyAccessToken"; var console = { log: function(s) { document.getElementById("console").innerHTML += s + "
" } } // Simple XMLHttpRequest // based on https://davidwalsh.name/xmlhttprequest SimpleRequest = { call: function(what, response) { var request; if (window.XMLHttpRequest) { // Mozilla, Safari, ... request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // Internet Explorer try { request = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { try { request = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {} } } // State changes request.onreadystatechange = function() { if (request.readyState === 4) { // Done if (request.status === 200) { // Complete response(request.responseText) } else response(); } } request.open('GET', what, true); request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken); request.send(null); } } //PromiseAll var promiseAll = function(items, block, done, fail) { var self = this; var promises = [], index = 0; items.forEach(function(item) { promises.push(function(item, i) { return new Promise(function(resolve, reject) { if (block) { block.apply(this, [item, index, resolve, reject]); } }); }(item, ++index)) }); Promise.all(promises).then(function AcceptHandler(results) { if (done) done(results); }, function ErrorHandler(error) { if (fail) fail(error); }); }; //promiseAll // LP: deferred execution block var ExecutionBlock = function(item, index, resolve, reject) { var url = "https://api.spotify.com/v1/" url += item; console.log( url ) SimpleRequest.call(url, function(result) { if (result) { var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) { return item.owner.href; }) resolve(profileUrls); } else { reject(new Error("call error")); } }) } arr = [ "search?type=playlist&q=%22doom%20metal%22", "search?type=playlist&q=Adele" ] promiseAll(arr, function(item, index, resolve, reject) { console.log("Making request [" + index + "]") ExecutionBlock(item, index, resolve, reject); }, function(results) { // Aggregated results console.log("All profiles received " + results.length); //console.log(JSON.stringify(results[0], null, 2)); ///// promiseall again var ExecutionProfileBlock = function(item, index, resolve, reject) { SimpleRequest.call(item, function(result) { if (result) { var obj = JSON.parse(result); resolve({ name: obj.display_name, followers: obj.followers.total, url: obj.href }); } //result }) } //ExecutionProfileBlock promiseAll(results[0], function(item, index, resolve, reject) { //console.log("Making request [" + index + "] " + item) ExecutionProfileBlock(item, index, resolve, reject); }, function(results) { // aggregated results console.log("All response received " + results.length); console.log(JSON.stringify(results, null, 2)); } , function(error) { // Error console.log(error); }) ///// }, function(error) { // Error console.log(error); });
 

2017 answer: you can now do exactly what you want in every current browser and node

This is quite simple:

  • Return a Promise
  • Use the ‘await’ , which will tell JavaScript to await the promise to be resolved into a value (like the HTTP response)
  • Add the ‘async’ keyword to the parent function

Here’s a working version of your code:

 (async function(){ var response = await superagent.get('...') console.log(response) })() 

await is supported in all current browsers and node 8

You can use this custom library (written using Promise) to make a remote call.

 function $http(apiConfig) { return new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open(apiConfig.method, apiConfig.url); client.send(); client.onload = function () { if (this.status >= 200 && this.status < 300) { // Performs the function "resolve" when this.status is equal to 2xx. // Your logic here. resolve(this.response); } else { // Performs the function "reject" when this.status is different than 2xx. reject(this.statusText); } }; client.onerror = function () { reject(this.statusText); }; }); } 

Simple usage example:

 $http({ method: 'get', url: 'google.com' }).then(function(response) { console.log(response); }, function(error) { console.log(error) }); 

Another solution is to execute code via sequential executor nsynjs .

If underlying function is promisified

nsynjs will evaluate all promises sequentially, and put promise result into data property:

 function synchronousCode() { var getURL = function(url) { return window.fetch(url).data.text().data; }; var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'; console.log('received bytes:',getURL(url).length); }; nsynjs.run(synchronousCode,{},function(){ console.log('synchronousCode done'); }); 
  

Js is a single threaded.

Browser can be divided into three parts:

1)Event Loop

2)Web API

3)Event Queue

Event Loop runs for forever ie kind of infinite loop.Event Queue is where all your function are pushed on some event(example:click) this is one by one carried out of queue and put into Event loop which execute this function and prepares it self for next one after first one is executed.This means Execution of one function doesn’t starts till the function before it in queue is executed in event loop.

Now let us think we pushed two functions in a queue one is for getting a data from server and another utilises that data.We pushed the serverRequest() function in queue first then utiliseData() function. serverRequest function goes in event loop and makes a call to server as we never know how much time it will take to get data from server so this process is expected to take time and so we busy our event loop thus hanging our page, that’s where Web API come into role it take this function from event loop and deals with server making event loop free so that we can execute next function from queue.The next function in queue is utiliseData() which goes in loop but because of no data available it goes waste and execution of next function continues till end of the queue.(This is called Async calling ie we can do something else till we get data)

Let suppose our serverRequest() function had a return statement in a code, when we get back data from server Web API will push it in queue at the end of queue. As it get pushed at end in queue we cannot utilise its data as there is no function left in our queue to utilise this data. Thus it is not possible to return something from Async Call.

Thus Solution to this is callback or promise .

A Image from one of the answers here, Correctly explains callback use… We give our function(function utilising data returned from server) to function calling server.

Llamar de vuelta

  function doAjax(callbackFunc, method, url) { var xmlHttpReq = new XMLHttpRequest(); xmlHttpReq.open(method, url); xmlHttpReq.onreadystatechange = function() { if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) { callbackFunc(xmlHttpReq.responseText); } } xmlHttpReq.send(null); } 

In my Code it is called as

 function loadMyJson(categoryValue){ if(categoryValue==="veg") doAjax(print,"GET","http://localhost:3004/vegetables"); else if(categoryValue==="fruits") doAjax(print,"GET","http://localhost:3004/fruits"); else console.log("Data not found"); } 

Read here for new methods in ECMA(2016/17) for making async call(@Felix Kling Answer on Top) https://stackoverflow.com/a/14220323/7579856

It’s a very common issue we face while struggling with the ‘mysteries’ of JavaScript. Let me try demystifying this mystery today.

Let’s start with a simple JavaScript function:

 function foo(){ // do something return 'wohoo'; } let bar = foo(); // bar is 'wohoo' here 

That’s a simple synchronous function call (where each line of code is ‘finished with its job’ before the next one in sequence), and the result is same as expected.

Now let’s add a bit of twist, by introducing little delay in our function, so that all lines of code are not ‘finished’ in sequence. Thus, it will emulate the asynchronous behavior of function :

 function foo(){ setTimeout( ()=>{ return 'wohoo'; }, 1000 ) } let bar = foo() // bar is undefined here 

So there you go, that delay just broke the functionality we expected! But what exactly happened ? Well, it’s actually pretty logical if you look at the code. the function foo() , upon execution, returns nothing (thus returned value is undefined ), but it does start a timer, which executes a function after 1s to return ‘wohoo’. But as you can see, the value that’s assigned to bar is the immediately returned stuff from foo(), not anything else that comes later.

So, how do we tackle this issue?

Let’s ask our function for a PROMISE . Promise is really about what it means : it means that the function guarantees you to provide with any output it gets in future. so let’s see it in action for our little problem above :

 function foo(){ return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something setTimeout ( function(){ // promise is RESOLVED , when execution reaches this line of code resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo' }, 1000 ) }) } let bar ; foo().then( res => { bar = res; console.log(bar) // will print 'wohoo' }); 

Thus, the summary is – to tackle the asynchronous functions like ajax based calls etc., you can use a promise to resolve the value (which you intend to return). Thus, in short you resolve value instead of returning , in asynchronous functions.

UPDATE (Promises with async/await)

Apart from using then/catch to work with promises, there exists one more approach. The idea is to recognize an asynchronous function and then wait for the promises to resolve, before moving to the next line of code. It’s still just the promises under the hood, but with a different syntactical approach. To make things clearer, you can find a comparison below:

then/catch version:

 function fetchUsers(){ let users = []; getUsers() .then(_users => users = _users) .catch(err =>{ throw err }) return users; } 

async/await version:

  async function fetchUsers(){ try{ let users = await getUsers() return users; } catch(err){ throw err; } } 

Here are some approaches to work with asynchronous requests:

  1. Browser Promise object
  2. Q – A promise library for JavaScript
  3. A+ Promises.js
  4. jQuery deferred
  5. XMLHttpRequest API
  6. Using callback concept – As implementation in first answer

Example: jQuery deferred implementation to work with multiple requests

 var App = App || {}; App = { getDataFromServer: function(){ var self = this, deferred = $.Deferred(), requests = []; requests.push($.getJSON('request/ajax/url/1')); requests.push($.getJSON('request/ajax/url/2')); $.when.apply(jQuery, requests).done(function(xhrResponse) { return deferred.resolve(xhrResponse.result); }); return deferred; }, init: function(){ this.getDataFromServer().done(_.bind(function(resp1, resp2) { // Do the operations which you wanted to do when you // get a response from Ajax, for example, log response. }, this)); } }; App.init(); 

Use a callback() function inside the foo() success. Try in this way. It is simple and easy to understand.

 var lat = ""; var lon = ""; function callback(data) { lat = data.lat; lon = data.lon; } function getLoc() { var url = "http://ip-api.com/json" $.getJSON(url, function(data) { callback(data); }); } getLoc(); 

ECMAScript 6 has ‘generators’ which allow you to easily program in an asynchronous style.

 function* myGenerator() { const callback = yield; let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback}); console.log("response is:", response); // examples of other things you can do yield setTimeout(callback, 1000); console.log("it delayed for 1000ms"); while (response.statusText === "error") { [response] = yield* anotherGenerator(); } } 

To run the above code you do this:

 const gen = myGenerator(); // Create generator gen.next(); // Start it gen.next((...args) => gen.next([...args])); // Set its callback function 

If you need to target browsers that don’t support ES6 you can run the code through Babel or closure-compiler to generate ECMAScript 5.

The callback ...args are wrapped in an array and destructured when you read them so that the pattern can cope with callbacks that have multiple arguments. For example with node fs :

 const [err, data] = yield fs.readFile(filePath, "utf-8", callback); 

Short answer : Your foo() method returns immediately, while the $ajax() call executes asynchronously after the function returns . The problem is then how or where to store the results retrieved by the async call once it returns.

Several solutions have been given in this thread. Perhaps the easiest way is to pass an object to the foo() method, and to store the results in a member of that object after the async call completes.

 function foo(result) { $.ajax({ url: '...', success: function(response) { result.response = response; // Store the async result } }); } var result = { response: null }; // Object to hold the async result foo(result); // Returns before the async completes 

Note that the call to foo() will still return nothing useful. However, the result of the async call will now be stored in result.response .

Of course there are many approaches like synchronous request, promise, but from my experience I think you should use the callback approach. It’s natural to asynchronous behavior of Javascript. So, your code snippet can be rewrite a little different:

 function foo() { var result; $.ajax({ url: '...', success: function(response) { myCallback(response); } }); return result; } function myCallback(response) { // Does something. } 

The question was:

How do I return the response from an asynchronous call?

which CAN be interpreted as:

How to make asynchronous code look synchronous ?

The solution will be to avoid callbacks, and use a combination of Promises and async/await .

I would like to give an example for a Ajax request.

(Although it can be written in Javascript, I prefer to write it in Python, and compile it to Javascript using Transcrypt . It will be clear enough.)

Lets first enable JQuery usage, to have $ available as S :

 __pragma__ ('alias', 'S', '$') 

Define a function which returns a Promise , in this case an Ajax call:

 def read(url: str): deferred = S.Deferred() S.ajax({'type': "POST", 'url': url, 'data': { }, 'success': lambda d: deferred.resolve(d), 'error': lambda e: deferred.reject(e) }) return deferred.promise() 

Use the asynchronous code as if it were synchronous :

 async def readALot(): try: result1 = await read("url_1") result2 = await read("url_2") except Exception: console.warn("Reading a lot failed") 

We find ourselves in a universe which appears to progress along a dimension we call “time”. We don’t really understand what time is, but we have developed abstractions and vocabulary that let us reason and talk about it: “past”, “present”, “future”, “before”, “after”.

The computer systems we build–more and more–have time as an important dimension. Certain things are set up to happen in the future. Then other things need to happen after those first things eventually occur. This is the basic notion called “asynchronicity”. In our increasingly networked world, the most common case of asynchonicity is waiting for some remote system to response to some request.

Consider an example. You call the milkman and order some milk. When it comes, you want to put it in your coffee. You can’t put the milk in your coffee right now, because it is not here yet. You have to wait for it to come before putting it in your coffee. In other words, the following won’t work:

 var milk = order_milk(); put_in_coffee(milk); 

Because JS has no way to know that it needs to wait for order_milk to finish before it executes put_in_coffee . In other words, it does not know that order_milk is asynchronous –is something that is not going to result in milk until some future time. JS, and other declarative languages, execute one statement after another without waiting.

The classic JS approach to this problem, taking advantage of the fact that JS supports functions as first-class objects which can be passed around, is to pass a function as a parameter to the asynchonous request, which it will then invoke when it has complete its task sometime in the future. That is the “callback” approach. Se parece a esto:

 order_milk(put_in_coffee); 

order_milk kicks off, orders the milk, then, when and only when it arrives, it invokes put_in_coffee .

The problem with this callback approach is that it pollutes the normal semantics of a function reporting its result with return ; instead, functions must nost reports their results by calling a callback given as a parameter. Also, this approach can rapidly become unwieldy when dealing with longer sequences of events. For example, let’s say that I want to wait for the milk to be put in the coffee, and then and only then perform a third step, namely drinking the coffee. I end up needing to write something like this:

 order_milk(function(milk) { put_in_coffee(milk, drink_coffee); } 

where I am passing to put_in_coffee both the milk to put in it, and also the action ( drink_coffee ) to execute once the milk has been put in. Such code becomes hard to write, and read, and debug.

In this case, we could rewrite the code in the question as:

 var answer; $.ajax('/foo.json') . done(function(response) { callback(response.data); }); function callback(data) { console.log(data); } 

Enter promises

This was the motivation for the notion of a “promise”, which is a particular type of value which represents a future or asynchronous outcome of some sort. It can represent something that already happened, or that is going to happen in the future, or might never happen at all. Promises have a single method, named then , to which you pass an action to be executed when the outcome the promise represents has been realized.

In the case of our milk and coffee, we design order_milk to return a promise for the milk arriving, then specify put_in_coffee as a then action, as follows:

 order_milk() . then(put_in_coffee) 

One advantage of this is that we can string these together to create sequences of future occurrences (“chaining”):

 order_milk() . then(put_in_coffee) . then(drink_coffee) 

Let’s apply promises to your particular problem. We will wrap our request logic inside a function, which returns a promise:

 function get_data() { return $.ajax('/foo.json'); } 

Actually, all we’ve done is added a return to the call to $.ajax . This works because jQuery’s $.ajax already returns a kind of promise-like thing. (In practice, without getting into details, we would prefer to wrap this call so as return a real promise, or use some alternative to $.ajax that does so.) Now, if we want to load the file and wait for it to finish and then do something, we can simply say

 get_data() . then(do_something) 

for instance,

 get_data() . then(function(data) { console.log(data); }); 

When using promises, we end up passing lots of functions into then , so it’s often helpful to use the more compact ES6-style arrow functions:

 get_data() . then(data => console.log(data)); 

The async keyword

But there’s still something vaguely dissatisfying about having to write code one way if synchronous and a quite different way if asynchronous. For synchronous, we write

 a(); b(); 

but if a is asynchronous, with promises we have to write

 a() . then(b); 

Above, we said “JS has no way to know that it needs to wait for the first call to finish before it executes the second”. Wouldn’t it be nice if there was some way to tell JS that? It turns out that there is–the await keyword, used inside a special type of function called an “async” function. This feature is part of the upcoming version of ES, but is already available in transpilers such as Babel given the right presets. This allows us to simply write

 async function morning_routine() { var milk = await order_milk(); var coffee = await put_in_coffee(milk); await drink(coffee); } 

In your case, you would be able to write something like

 async function foo() { data = await get_data(); console.log(data); } 

Using ES2017 you should have this as the function declaration

 async function foo() { var response = await $.ajax({url: '...'}) return response; } 

And executing it like this.

 (async function() { try { var result = await foo() console.log(result) } catch (e) {} })() 

Or the Promise syntax

 foo().then(response => { console.log(response) }).catch(error => { console.log(error) }) 

Let’s see the forest first before looking at the trees.

There are many informative answers with great details here, I won’t repeat any of them. The key to programming in JavaScript is having first the correct mental model of overall execution.

  1. Your entry point(s) is executed as the result of an event. For example, a script tag with code is loaded into the browser. (Accordingly, this is why you may need to be concerned with the readiness of the page to run your code if it requires dom elements to be constructed first, etc.)
  2. Your code executes to completion–however many asynchronous calls it makes–without executing any of your callbacks, including XHR requests, set timeouts, dom event handlers, etc. Each of those callbacks waiting to be executed will sit in a queue, waiting their turn to be run after other events that fired have all finished execution.
  3. Each individual callback to an XHR request, set timeout or dom the event once invoked will then run to completion.

The good news is that if you understand this point well, you will never have to worry about race conditions. You should first and foremost thing of how you want to organize your code as essentially the response to different discrete events, and how you want to thread them together into a logical sequence. You can use promises or higher level new async/await as tools to that end, or you can roll your own.

But you shouldn’t use any tactical tools to solve a problem until you are comfortable with the actual problem domain. Draw a map of these dependencies to know what needs to run when. Attempting an ad-hoc approach to all these callbacks is just not going to serve you well.

Rather than throwing code at you, there are 2 concepts that are key to understanding how JS handles callbacks and asynchronicity. (is that even a word?)

The Event Loop and Concurrency Model

There are three things you need to be aware of; The queue; the event loop and the stack

In broad, simplistic terms, the event loop is like the project manager, it is constantly listening for any functions that want to run and communicates between the queue and the stack.

 while (queue.waitForMessage()) { queue.processNextMessage(); } 

Once it receives a message to run something it adds it to the queue. The queue is the list of things that are waiting to execute (like your AJAX request). imagine it like this:

  1. call foo.com/api/bar using foobarFunc 2. Go perform an infinite loop ... and so on 

When one of these messages is going to execute it pops the message from the queue and creates a stack, the stack is everything JS needs to execute to perform the instruction in the message. So in our example it’s being told to call foobarFunc

 function foobarFunc (var) { console.log(anotherFunction(var)); } 

So anything that foobarFunc needs to execute (in our case anotherFunction ) will get pushed onto the stack. executed, and then forgotten about – the event loop will then move onto the next thing in the queue (or listen for messages)

The key thing here is the order of execution. Es decir

WHEN is something going to run

When you make a call using AJAX to an external party or run any asynchronous code (a setTimeout for example), Javascript is dependant upon a response before it can proceed.

The big question is when will it get the response? The answer is we don’t know – so the event loop is waiting for that message to say “hey run me”. If JS just waited around for that message synchronously your app would freeze and it will suck. So JS carries on executing the next item in the queue whilst waiting for the message to get added back to the queue.

That’s why with asynchronous functionality we use things called callbacks . It’s kinda like a promise quite literally. As in I promise to return something at some point jQuery uses specific callbacks called deffered.done deffered.fail and deffered.always (amongst others). You can see them all here

So what you need to do is pass a function that is promised to execute at some point with data that is passed to it.

Because a callback is not executed immediately but at a later time it’s important to pass the reference to the function not it executed. asi que

 function foo(bla) { console.log(bla) } 

so most of the time (but not always) you’ll pass foo not foo()

Hopefully that will make some sense. When you encounter things like this that seem confusing – i highly recommend reading the documentation fully to at least get an understanding of it. It will make you a much better developer.