Comment renvoyer la réponse d’un appel asynchrone?

J’ai une fonction foo qui fait une demande Ajax. Comment puis-je renvoyer la réponse de foo ?

J’ai essayé de renvoyer la valeur du rappel de success d’atsortingbuer la réponse à une variable locale de la fonction et de la renvoyer, mais aucune de ces méthodes ne renvoie la réponse.

 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`. 

→ Pour une explication plus générale du comportement asynchrone avec différents exemples, voir Pourquoi ma variable est-elle inchangée après que je l’ai modifiée à l’intérieur d’une fonction? – Référence de code asynchrone

→ Si vous comprenez déjà le problème, passez aux solutions possibles ci-dessous.

Le problème

Le A dans Ajax signifie asynchrone . Cela signifie que l’envoi de la demande (ou plutôt la réception de la réponse) est retiré du stream d’exécution normal. Dans votre exemple, $.ajax retourne immédiatement et l’instruction suivante, return result; , est exécuté avant même que la fonction que vous avez transmise en tant que rappel de success n’ait été appelée.

Voici une analogie qui permet, espérons-le, de faire la différence entre les stream synchrone et asynchrone:

Synchrone

Imaginez que vous appeliez un ami et lui demandiez de rechercher quelque chose pour vous. Bien que cela puisse prendre un certain temps, vous attendez au téléphone et fixez l’espace, jusqu’à ce que votre ami vous donne la réponse dont vous aviez besoin.

La même chose se produit lorsque vous effectuez un appel de fonction contenant le code “normal”:

 function findItem() { var item; while(item_not_found) { // search } return item; } var item = findItem(); // Do something with item doSomethingElse(); 

Même si findItem peut prendre beaucoup de temps à s’exécuter, le code suivant var item = findItem(); doit attendre que la fonction retourne le résultat.

Asynchrone

Vous appelez à nouveau votre ami pour la même raison. Mais cette fois, vous lui dites que vous êtes pressé et qu’il devrait vous rappeler sur votre téléphone portable. Vous raccrochez, quittez la maison et faites ce que vous aviez prévu de faire. Une fois que votre ami vous rappelle, vous traitez avec les informations qu’il vous a données.

C’est exactement ce qui se passe lorsque vous faites une demande Ajax.

 findItem(function(item) { // Do something with item }); doSomethingElse(); 

Au lieu d’attendre la réponse, l’exécution continue immédiatement et l’instruction après l’exécution de l’appel Ajax. Pour obtenir la réponse éventuellement, vous fournissez une fonction à appeler une fois la réponse reçue, un rappel (notez quelque chose? Rappelez ?). Toute instruction venant après cet appel est exécutée avant l’appel du rappel.


Solutions)

Emarmsez la nature asynchrone de JavaScript! Bien que certaines opérations asynchrones fournissent des contreparties synchrones (tout comme “Ajax”), il est généralement déconseillé de les utiliser, en particulier dans un contexte de navigateur.

Pourquoi est-ce mauvais, demandez-vous?

JavaScript s’exécute dans le fil de l’interface utilisateur du navigateur et tout processus de longue durée verrouille l’interface utilisateur, ce qui le rend inactif. En outre, le temps d’exécution de JavaScript est limité et le navigateur demande à l’utilisateur s’il souhaite ou non poursuivre l’exécution.

Tout cela constitue une très mauvaise expérience utilisateur. L’utilisateur ne pourra pas dire si tout fonctionne bien ou pas. En outre, l’effet sera pire pour les utilisateurs ayant une connexion lente.

Dans ce qui suit, nous examinerons trois solutions différentes qui se construisent les unes sur les autres:

  • Promises avec async/await wait (ES2017 +, disponible dans les anciens navigateurs si vous utilisez un transstackr ou un régénérateur)
  • Callbacks (populaire dans le noeud)
  • Promises with then() (ES2015 +, disponible dans les navigateurs plus anciens si vous utilisez l’une des nombreuses bibliothèques de promesses)

Tous les trois sont disponibles dans les navigateurs actuels et sur le nœud 7+.


ES2017 +: promesses avec async/await

La version ECMAScript publiée en 2017 a introduit un support au niveau syntaxique pour les fonctions asynchrones. Avec async et await , vous pouvez écrire de manière asynchrone dans un “style synchrone”. Le code est toujours asynchrone, mais il est plus facile à lire / à comprendre.

async/await construit au-dessus des promesses: une fonction async renvoie toujours une promesse. await “ouvre” une promesse et aboutit à la valeur avec laquelle la promesse a été résolue ou génère une erreur si la promesse est rejetée.

Important: Vous ne pouvez utiliser await dans une fonction async . Cela signifie qu’au plus haut niveau, vous devez toujours travailler directement avec la promesse.

Vous pouvez en savoir plus sur async et await sur MDN.

Voici un exemple qui se base sur le délai ci-dessus:

 // 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.ssortingngify(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); }); 

Les versions actuelles du navigateur et du nœud prennent en charge async/await . Vous pouvez également prendre en charge des environnements plus anciens en transformant votre code en ES5 à l’aide de regenerator (ou d’outils utilisant un régénérateur, tel que Babel ).


Laisser les fonctions accepter les rappels

Un rappel est simplement une fonction transmise à une autre fonction. Cette autre fonction peut appeler la fonction transmise à chaque fois qu’elle est prête. Dans le contexte d’un processus asynchrone, le rappel sera appelé chaque fois que le processus asynchrone est terminé. Habituellement, le résultat est transmis au rappel.

Dans l’exemple de la question, vous pouvez faire en sorte que foo accepte un rappel et l’utilise comme rappel de success . Donc ça

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

devient

 foo(function(result) { // Code that depends on 'result' }); 

Ici nous avons défini la fonction “inline” mais vous pouvez passer n’importe quelle référence de fonction:

 function myCallback(result) { // Code that depends on 'result' } foo(myCallback); 

foo lui-même est défini comme suit:

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

callback fera référence à la fonction que nous passons à foo lorsque nous l’appelons et nous la transmettons simplement au success . C’est-à-dire qu’une fois la requête Ajax réussie, $.ajax appellera callback et transmettra la réponse au callback (auquel on peut faire référence avec result , puisque c’est ainsi que nous avons défini le rappel).

Vous pouvez également traiter la réponse avant de la transmettre au rappel:

 function foo(callback) { $.ajax({ // ... success: function(response) { // For example, filter the response callback(filtered_response); } }); } 

Il est plus facile d’écrire du code à l’aide de rappels que cela puisse paraître. Après tout, JavaScript dans le navigateur est fortement basé sur les événements (événements DOM). Recevoir la réponse Ajax n’est rien d’autre qu’un événement.
Des difficultés peuvent survenir lorsque vous devez travailler avec du code tiers, mais la plupart des problèmes peuvent être résolus en réfléchissant simplement au stream d’applications.


ES2015 +: promesses avec then ()

L’ API Promise est une nouvelle fonctionnalité d’ECMAScript 6 (ES2015), mais elle est déjà prise en charge par les navigateurs . De nombreuses bibliothèques implémentent l’API standard Promises et fournissent des méthodes supplémentaires pour faciliter l’utilisation et la composition de fonctions asynchrones (par exemple, bluebird ).

Les promesses sont des conteneurs pour les valeurs futures . Lorsque la promesse reçoit la valeur (elle est résolue ) ou lorsqu’elle est annulée ( rejetée ), elle en avertit tous ses “auditeurs” qui souhaitent accéder à cette valeur.

L’avantage par rapport aux rappels en clair est qu’ils vous permettent de découpler votre code et qu’ils sont plus faciles à composer.

Voici un exemple simple d’utilisation d’une promesse:

 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). }); 

Appliqué à notre appel Ajax, nous pourrions utiliser des promesses comme celle-ci:

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

Décrire tous les avantages offerts par Promet est au-delà de la scope de cette réponse, mais si vous écrivez un nouveau code, vous devez l’envisager sérieusement. Ils fournissent une grande abstraction et séparation de votre code.

Plus d’informations sur les promesses: HTML5 roches – JavaScript Promises

Note latérale: Les objects différés de jQuery

Les objects différés constituent l’implémentation personnalisée de promesses par jQuery (avant que l’API Promise ne soit standardisée). Ils se comportent presque comme des promesses mais exposent une API légèrement différente.

Chaque méthode Ajax de jQuery renvoie déjà un “object différé” (en fait une promesse d’object différé) que vous pouvez simplement renvoyer à partir de votre fonction:

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

Note latérale: les promesses

N’oubliez pas que les promesses et les objects différés ne sont que des conteneurs pour une valeur future, ils ne sont pas la valeur elle-même. Par exemple, supposons que vous ayez:

 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 } 

Ce code comprend mal les problèmes d’asynchronie ci-dessus. Plus précisément, $.ajax() ne fige pas le code pendant la vérification de la page ‘/ mot de passe’ sur votre serveur – il envoie une requête au serveur et pendant qu’il attend, renvoie immédiatement un object jQuery Ajax Deferred, et non la réponse de le serveur. Cela signifie que l’instruction if obtiendra toujours cet object différé, la traitera comme true et procédera comme si l’utilisateur était connecté. Pas bon.

Mais la solution est facile:

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

Non recommandé: appels “Ajax” synchrones

Comme je l’ai mentionné, certaines (!) Opérations asynchrones ont des contreparties synchrones. Je ne préconise pas leur utilisation, mais par souci d’exhaustivité, voici comment procéder pour effectuer un appel synchrone:

Sans jQuery

Si vous utilisez directement un object XMLHTTPRequest , transmettez false comme troisième argument à .open .

jQuery

Si vous utilisez jQuery , vous pouvez définir l’option async sur false . Notez que cette option est obsolète depuis jQuery 1.8. Vous pouvez alors toujours utiliser un rappel de success ou accéder à la propriété responseText de l’ object jqXHR :

 function foo() { var jqXHR = $.ajax({ //... async: false }); return jqXHR.responseText; } 

Si vous utilisez une autre méthode Ajax jQuery, telle que $.get , $.getJSON , etc., vous devez la remplacer par $.ajax (car vous ne pouvez transmettre que les parameters de configuration à $.ajax ).

La tête haute! Il n’est pas possible de faire une demande JSONP synchrone. JSONP est par nature toujours asynchrone (une raison de plus pour ne même pas envisager cette option).

Si vous n’utilisez pas jQuery dans votre code, cette réponse est pour vous.

Votre code devrait être quelque chose dans le sens de ceci:

 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 a très bien écrit les réponses aux personnes utilisant jQuery pour AJAX. J’ai décidé de proposer une solution de remplacement aux personnes qui ne le sont pas.

( Remarque, pour ceux qui utilisent la nouvelle API d’ fetch , Angular ou les promesses, j’ai ajouté une autre réponse ci-dessous )


À quoi tu fais face

Ceci est un court résumé de “Explication du problème” de l’autre réponse. Si vous n’êtes pas sûr après avoir lu ceci, lisez-le.

Le A en AJAX signifie asynchrone . Cela signifie que l’envoi de la demande (ou plutôt la réception de la réponse) est retiré du stream d’exécution normal. Dans votre exemple, .send retourne immédiatement et l’instruction suivante, return result; , est exécuté avant même que la fonction que vous avez transmise en tant que rappel de success n’ait été appelée.

Cela signifie que lorsque vous revenez, l’écouteur que vous avez défini ne s’est pas encore exécuté, ce qui signifie que la valeur que vous renvoyez n’a pas encore été définie.

Voici une analogie simple

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

(Violon)

La valeur de a renvoyé est undefined car la partie a a=5 n’a pas encore été exécutée. AJAX agit comme ceci: vous renvoyez la valeur avant que le serveur ait la chance de dire à votre navigateur quelle est cette valeur.

Une solution possible à ce problème consiste à coder de manière réactive , en indiquant à votre programme quoi faire lorsque le calcul est terminé.

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

Ceci s’appelle CPS . En gros, nous transmettons à getFive une action à effectuer à la fin, nous getFive à notre code comment réagir à la fin d’un événement (comme notre appel AJAX ou, dans ce cas, le délai d’expiration).

L’utilisation serait:

 getFive(onComplete); 

Ce qui devrait alerter “5” sur l’écran. (Violon)

Solutions possibles

Il existe fondamentalement deux façons de résoudre ce problème:

  1. Rendre l’appel AJAX synchrone (appelons-le SJAX).
  2. Restructurez votre code pour qu’il fonctionne correctement avec les rappels.

1. AJAX synchrone – Ne le faites pas!

Quant à AJAX synchrone, ne le faites pas! La réponse de Felix soulève des arguments convaincants pour expliquer pourquoi c’est une mauvaise idée. Pour résumer, cela gèlera le navigateur de l’utilisateur jusqu’à ce que le serveur renvoie la réponse et créera une très mauvaise expérience utilisateur. Voici un autre court résumé tiré de MDN expliquant pourquoi:

XMLHttpRequest prend en charge les communications synchrones et asynchrones. En général, cependant, les demandes asynchrones doivent être préférées aux demandes synchrones pour des raisons de performances.

En bref, les requêtes synchrones bloquent l’exécution du code … … cela peut causer de sérieux problèmes …

Si vous devez le faire, vous pouvez passer un drapeau: Voici comment:

 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. Code de restructuration

Laissez votre fonction accepter un rappel. Dans l’exemple, le code foo peut être configuré pour accepter un rappel. Nous dirons à notre code comment réagir lorsque foo terminé.

Alors:

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

Devient:

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

Ici, nous avons passé une fonction anonyme, mais nous pourrions tout aussi facilement passer une référence à une fonction existante, en lui donnant l’aspect suivant:

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

Pour plus de détails sur la manière dont ce type de conception de rappel est effectué, consultez la réponse de Felix.

Maintenant, définissons foo lui-même pour agir en conséquence

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

(violon)

Nous avons maintenant forcé notre fonction foo à accepter une action à exécuter lorsque AJAX se termine correctement. Nous pouvons l’étendre davantage en vérifiant si le statut de la réponse n’est pas 200 et en agissant en conséquence (création d’un gestionnaire d’échec, etc.). Résoudre efficacement notre problème.

Si vous avez encore du mal à comprendre cela, lisez le Guide de démarrage AJAX de MDN.

XMLHttpRequest 2 (tout d’abord, lisez les réponses de Benjamin Gruenbaum et Felix Kling)

Si vous n’utilisez pas jQuery et que vous voulez un joli court XMLHttpRequest 2 qui fonctionne sur les navigateurs modernes ainsi que sur les navigateurs mobiles, je suggère de l’utiliser de cette façon:

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

Comme vous pouvez le voir:

  1. C’est plus court que toutes les autres fonctions listées.
  2. Le rappel est défini directement (donc pas de fermetures inutiles supplémentaires).
  3. Il utilise le nouveau onload (vous n’avez donc pas besoin de vérifier l’état de readystate &&)
  4. Il y a quelques autres situations dont je ne me souviens pas qui rendent XMLHttpRequest 1 ennuyeux.

Il existe deux manières d’obtenir la réponse de cet appel Ajax (trois à l’aide du nom XMLHttpRequest var):

Le plus simple:

 this.response 

Ou si pour une raison quelconque vous bind() le rappel à une classe:

 e.target.response 

Exemple:

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

Ou (celle ci-dessus est meilleure, les fonctions anonymes sont toujours un problème):

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

Rien de plus facile.

Maintenant, certaines personnes diront probablement qu’il vaut mieux utiliser onreadystatechange ou même le nom de variable XMLHttpRequest. C’est faux.

Découvrez les fonctionnalités avancées de XMLHttpRequest

Il a supporté sur tous les * navigateurs modernes. Et je peux confirmer que j’utilise cette approche puisque XMLHttpRequest 2 existe. Je n’ai jamais eu aucun type de problème sur tous les navigateurs que j’utilise.

onreadystatechange n’est utile que si vous souhaitez obtenir les en-têtes sur l’état 2.

L’utilisation du nom de variable XMLHttpRequest est une autre grosse erreur, car vous devez exécuter le rappel à l’intérieur des fermetures onload / oreadystatechange, sinon vous l’avez perdu.


Maintenant, si vous voulez quelque chose de plus complexe en utilisant post et FormData, vous pouvez facilement étendre cette fonction:

 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) } 

Encore une fois … c’est une fonction très courte, mais elle reçoit et publie.

Exemples d’utilisation:

 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 

Ou passez un élément de formulaire complet ( document.getElementsByTagName('form')[0] ):

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

Ou définissez des valeurs personnalisées:

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

Comme vous pouvez le constater, je n’ai pas implémenté la synchronisation … c’est une mauvaise chose.

Cela dit, pourquoi ne pas le faire facilement?


Comme mentionné dans le commentaire, l’utilisation de error && synchrone fait complètement éclater le sharepoint la réponse. Quel est le moyen le plus rapide d’utiliser correctement Ajax?

Gestionnaire d’erreur

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

Dans le script ci-dessus, vous avez un gestionnaire d’erreur qui est défini de manière statique afin de ne pas compromettre la fonction. Le gestionnaire d’erreurs peut également être utilisé pour d’autres fonctions.

Mais pour vraiment sortir une erreur, le seul moyen est d’écrire une mauvaise URL, auquel cas chaque navigateur génère une erreur.

Les gestionnaires d’erreurs sont peut-être utiles si vous définissez des en-têtes personnalisés, définissez le responseType sur le tampon de tableau blob ou autre ….

Même si vous passez “POSTAPAPAP” comme méthode, cela ne produira pas d’erreur.

Même si vous transmettez ‘fdggdgilfdghfldj’ en tant que formdata, cela ne produira pas d’erreur.

Dans le premier cas, l’erreur est à l’intérieur de la Method not Allowed this.statusText displayAjax() sous this.statusText as Method not Allowed .

Dans le second cas, cela fonctionne simplement. Vous devez vérifier côté serveur si vous avez transmis les bonnes données de publication.

inter-domaine non autorisé génère une erreur automatiquement.

Dans la réponse d’erreur, il n’y a pas de codes d’erreur.

Il n’y a que le type this.type qui est défini sur error.

Pourquoi append un gestionnaire d’erreurs si vous n’avez aucun contrôle sur les erreurs? La plupart des erreurs sont renvoyées à l’intérieur dans la fonction de rappel displayAjax() .

Donc: pas besoin de vérification d’erreur si vous êtes capable de copier et coller l’URL correctement. 😉

PS: En tant que premier test, j’ai écrit x (‘x’, displayAjax) …, et la réponse a été complète… ??? J’ai donc vérifié le dossier dans lequel se trouve le code HTML et il y avait un fichier appelé ‘x.xml’. Donc, même si vous oubliez l’extension de votre fichier, XMLHttpRequest 2 le trouvera . J’ai raté


Lire un fichier synchrone

Ne fais pas ça.

Si vous voulez bloquer le navigateur pendant un moment, chargez un beau gros fichier txt synchrone.

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

Maintenant tu peux faire

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

Il n’y a pas d’autre moyen de faire cela de manière non asynchrone. (Oui, avec la boucle setTimeout … mais sérieusement?)

Un autre point est … si vous travaillez avec des API ou si vous possédez uniquement des fichiers de liste ou quoi que ce soit, vous utilisez toujours des fonctions différentes pour chaque requête …

Seulement si vous avez une page sur laquelle vous chargez toujours le même XML / JSON ou tout ce dont vous n’avez besoin que d’une seule fonction. Dans ce cas, modifiez un peu la fonction Ajax et remplacez b par votre fonction spéciale.


Les fonctions ci-dessus sont pour une utilisation de base.

Si vous voulez étendre la fonction …

Oui, vous pouvez.

J’utilise beaucoup d’API et l’une des premières fonctions que j’intègre dans chaque page HTML est la première fonction Ajax dans cette réponse, avec uniquement GET …

Mais vous pouvez faire beaucoup de choses avec XMLHttpRequest 2:

J’ai créé un gestionnaire de téléchargement (en utilisant des plages des deux côtés avec resume, filereader, système de fichiers), divers convertisseurs de redimensionneurs d’images utilisant canvas, peuplant des bases de données websql avec base64images et bien plus encore … Mais dans ces cas, vous devez créer une fonction uniquement dans ce but … parfois vous avez besoin d’un blob, de tampons de tableau, vous pouvez définir des en-têtes, remplacer le type MIME et bien plus encore …

Mais la question ici est de savoir comment renvoyer une réponse Ajax … (j’ai ajouté un moyen simple).

Si vous utilisez des promesses, cette réponse est pour vous.

Cela signifie AngularJS, jQuery (avec différé), le remplacement natif de XHR (fetch), EmberJS, la sauvegarde de BackboneJS ou toute bibliothèque de noeuds renvoyant des promesses.

Votre code devrait être quelque chose dans le sens de ceci:

 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 a très bien écrit les réponses aux personnes utilisant jQuery avec des rappels pour AJAX. J’ai une réponse pour XHR native. Cette réponse concerne l’utilisation générique des promesses, que ce soit en amont ou en aval.


La question centrale

Le modèle de concurrence de JavaScript dans le navigateur et sur le serveur avec NodeJS / io.js est asynchrone et réactif .

Chaque fois que vous appelez une méthode qui renvoie une promesse, les gestionnaires then sont toujours exécutés de manière asynchrone, c’est-à-dire qu’après le code situé en dessous d’eux, ils ne .then pas dans un gestionnaire .then .

Cela signifie que lorsque vous renvoyez des data le gestionnaire then défini n’a pas encore été exécuté. Cela signifie à son tour que la valeur que vous renvoyez n’a pas été définie correctement dans le temps.

Voici une analogie simple pour le problème:

  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.

C’est:

 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.ssortingngify(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.

entrez la description de l'image ici

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: ssortingng) { 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. 

Exemple:

 // 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 interestd 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.

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

 [ "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.ssortingngify(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.ssortingngify(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.

CallBack

  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-comstackr 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 resortingeved 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 comstack 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. Cela ressemble à ceci:

 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 ssortingng 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 transstackrs 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. That is

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. alors

 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.