javascript - success - récupérer résultat requete ajax




Comment renvoyer la réponse d'un appel asynchrone? (20)

→ 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 asynchronous . Cela signifie que l'envoi de la demande (ou plutôt la réception de la réponse) est retiré du flux 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 flux 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)

Embrassez 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 transpiler 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/await 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.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);
  });

Les versions actuelles du browser et du node 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 flux d'applications.

ES2015 +: promesses avec then()

L' then() 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 portée 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 objets différés de jQuery

Les objets 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 "objet différé" (en fait une promesse d'objet 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 objets 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 objet jQuery Ajax Deferred, et non la réponse de le serveur. Cela signifie que l'instruction if obtiendra toujours cet objet 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 objet 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' objet 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 paramètres 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).

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

Js est un single threaded.

Le navigateur peut être divisé en trois parties:

1) Boucle d'événement

2) API Web

3) file d'attente d'événements

La boucle d'événement est exécutée pour toujours, c'est-à-dire comme une boucle infinie. La file d'attente d'événement est l'endroit où toutes vos fonctions sont poussées sur un événement (exemple: clic). pour l'exécution suivante après exécution, cela signifie que l'exécution d'une fonction ne commence que lorsque celle-ci est exécutée dans la boucle d'événement.

Supposons maintenant que nous avons placé deux fonctions dans une file d’attente, l’une pour obtenir des données du serveur et une autre utilisant ces données. Nous avons poussé la fonction serverRequest () en file d’attente, puis la fonction utiliseData (). La fonction serverRequest passe en boucle d'événement et appelle le serveur car nous ne savons jamais combien de temps il faudra pour obtenir les données du serveur. Ce processus devrait donc prendre du temps. Nous occupons donc notre boucle d'événements en suspendant notre page, c'est là que le Web Les API entrent dans le rôle qu’elles prennent cette fonction de la boucle d’événement et traitent de la création d’une boucle d’événement libre du serveur afin que nous puissions exécuter la fonction suivante de la file d’attente. le gaspillage et l'exécution de la fonction suivante se poursuivent jusqu'à la fin de la file d'attente (cela s'appelle un appel asynchrone, c'est-à-dire que nous pouvons faire autre chose jusqu'à ce que nous obtenions des données).

Supposons que notre fonction serverRequest () contienne une instruction return dans un code. Lorsque nous récupérons les données de l'API Web du serveur, elles sont placées dans la file d'attente à la fin de la file d'attente. Comme il est poussé à la fin de la file d'attente, nous ne pouvons pas utiliser ses données, car il ne reste plus aucune fonction dans notre file d'attente pour utiliser ces données. Il n’est donc pas possible de renvoyer quelque chose d’appel asynchrone.

Ainsi, la solution est un rappel ou une promesse .

Une image de l'une des réponses ici, explique correctement l'utilisation du rappel ... Nous donnons notre fonction (fonction utilisant les données renvoyées par le serveur) au serveur appelant.

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

}

Dans mon code, il est appelé

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

Lisez ici les nouvelles méthodes dans ECMA (2016/17) pour passer un appel asynchrone (@Felix Kling Answer on Top) https://.com/a/14220323/7579856


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

La valeur de data est undefined car la partie data = 5 n'a pas encore été exécutée. Il sera probablement exécuté dans une seconde, mais à ce moment-là, il n’est plus pertinent pour la valeur renvoyée.

Étant donné que l'opération ne s'est pas encore produite (AJAX, appel sur le serveur, IO, timer), vous renvoyez la valeur avant que la demande ait la possibilité d'indiquer à votre code 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é. Les promesses le permettent activement en étant de nature temporelle (sensible au temps).

Récapitulation rapide des promesses

Une promesse est une valeur dans le temps . Les promesses ont un état, elles démarrent en attente sans valeur et peuvent se régler comme suit:

  • accompli ce qui signifie que le calcul est terminé avec succès.
  • rejeté ce qui signifie que le calcul a échoué.

Une promesse ne peut changer d’états qu’une seule fois, après quoi elle restera toujours dans le même état pour toujours. Vous pouvez then associer les gestionnaires aux promesses d’extraire leur valeur et de gérer les erreurs. then gestionnaires permettent l' chaining des appels. Les promesses sont créées en utilisant des API qui les renvoient . Par exemple, l' fetch plus moderne de remplacement AJAX ou les promesses de retour $.get jQuery.

Lorsque nous appelons .then une promesse et nous retournons quelque chose, nous obtenons une promesse pour la valeur traitée . Si nous retournons une autre promesse, nous obtiendrons des choses incroyables, mais tenons nos chevaux.

Avec des promesses

Voyons comment nous pouvons résoudre le problème ci-dessus avec des promesses. Tout d'abord, démontrons notre compréhension des états de promesse d'en haut en utilisant le constructeur Promise pour créer une fonction de retard:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Maintenant, après avoir converti setTimeout pour utiliser des promesses, nous pouvons utiliser thenpour le faire compter:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

En fait, au lieu de retourner une valeur que nous ne pouvons le faire en raison du modèle de concurrence - nous retourner une enveloppe pour une valeur que nous pouvons déballer avec then. C'est comme une boîte avec laquelle vous pouvez ouvrir then.

Appliquer cette

Il en va de même pour votre appel d'API d'origine, vous pouvez:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Donc, cela fonctionne aussi bien. Nous avons appris que nous ne pouvons pas renvoyer de valeurs d'appels déjà asynchrones, mais que nous pouvons utiliser des promesses et les chaîner pour effectuer le traitement. Nous savons maintenant comment renvoyer la réponse d'un appel asynchrone.

ES2015 (ES6)

ES6 introduit des generators qui sont des fonctions qui peuvent revenir au milieu, puis reprendre leur position actuelle. Ceci est généralement utile pour les séquences, par exemple:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Est une fonction qui renvoie un itérateur sur la séquence 1,2,3,3,3,3,....pouvant être itérée. Bien que cela soit intéressant en lui-même et laisse beaucoup de possibilités, il existe un cas intéressant en particulier.

Si la séquence que nous produisons est une séquence d'actions plutôt que de chiffres, nous pouvons suspendre la fonction chaque fois qu'une action est générée et l'attendre avant de reprendre la fonction. Ainsi, au lieu d’une suite de nombres, nous avons besoin d’une suite de valeurs futures , c’est-à-dire: des promesses.

Cette astuce un peu délicate mais très puissante nous permet d’écrire du code asynchrone de manière synchrone. Il y a plusieurs "coureurs" qui le font pour vous. Écrire une ligne ne représente que quelques lignes de code, mais dépasse le cadre de cette réponse. Je vais utiliser Bluebird Promise.coroutineici, mais il y a d'autres enveloppeurs comme coou Q.async.

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Cette méthode retourne une promesse elle-même, que nous pouvons consommer d'autres coroutines. Par exemple:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

Dans ES7, ceci est davantage standardisé, il y a plusieurs propositions pour le moment mais vous pouvez toutes les awaitpromettre. Ceci est juste "sucre" (syntaxe plus agréable) pour la proposition ES6 ci-dessus en ajoutant les mots clés asyncet await. Voici l'exemple ci-dessus:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Il retourne toujours une promesse quand même :)


Réponse de 2017: vous pouvez désormais faire exactement ce que vous voulez dans tous les navigateurs et nœuds actuels

C'est assez simple:

  • Retourner une promesse
  • Utilisez l' await , qui indiquera à JavaScript d'attendre que la promesse soit résolue en une valeur (comme la réponse HTTP)
  • Ajoutez le mot clé async/await à la fonction parent

Voici une version de travail de votre code:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

wait est pris en charge par tous les navigateurs et nœuds 8 actuels.


Voici quelques approches pour travailler avec des requêtes asynchrones:

  1. then()
  2. Q - Une bibliothèque de promesses pour JavaScript
  3. A + Promises.js
  4. jQuery différé
  5. API XMLHttpRequest
  6. Utilisation du concept de rappel - Comme implémentation en première réponse

Exemple: l'implémentation différée de jQuery fonctionnera avec plusieurs requêtes

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


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 point de 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 ajouter 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 à cette fin. ... 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).


Regardez cet exemple:

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

Comme vous pouvez le voir getJokeest le retour d' une résolution promesse (il est résolu lors du retour res.data.value). Vous attendez donc que la requête $ http.get soit terminée, puis que console.log (res.joke) soit exécuté (en tant que flux asynchrone normal).

C'est le plnkr:

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


Voyons d'abord la forêt avant de regarder les arbres.

Il y a beaucoup de réponses informatives avec beaucoup de détails ici, je ne vais pas les répéter. La clé de la programmation en JavaScript est d’avoir d’abord le bon modèle mental d’exécution globale.

  1. Votre point d'entrée est exécuté à la suite d'un événement. Par exemple, une balise de script avec du code est chargée dans le navigateur. (En conséquence, c’est pourquoi vous devrez peut-être vous préoccuper de la disponibilité de la page à exécuter votre code si elle nécessite la construction préalable d’éléments dom, etc.)
  2. Votre code est exécuté jusqu'à son terme - quel que soit le nombre d'appels asynchrones qu'il effectue - sans exécuter aucun de vos rappels, y compris les demandes XHR, les délais impartis, les gestionnaires d'événement dom, etc. à leur tour d'être exécutés après que d'autres événements qui ont été déclenchés ont tous été exécutés.
  3. Chaque rappel individuel à une demande XHR, à l'expiration ou à la clôture de l'événement une fois invoqué, sera ensuite exécuté jusqu'à son terme.

La bonne nouvelle est que si vous comprenez bien ce point, vous ne devrez jamais vous inquiéter des conditions de course. Vous devez avant tout définir la manière dont vous souhaitez organiser votre code comme étant essentiellement la réponse à différents événements discrets et la manière dont vous souhaitez les relier dans une séquence logique. Vous pouvez utiliser des promesses ou de nouvelles versions asynchrones / wait de niveau supérieur comme outils à cette fin, ou vous pouvez créer les vôtres.

Mais vous ne devriez utiliser aucun outil tactique pour résoudre un problème tant que vous n'êtes pas à l'aise avec le domaine du problème actuel. Dessinez une carte de ces dépendances pour savoir ce qui doit être exécuté quand. Tenter une approche ad hoc de tous ces rappels ne vous servira à rien.


ECMAScript 6 possède des «générateurs» qui vous permettent de programmer facilement dans un style asynchrone.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://.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();
    }
}

Pour exécuter le code ci-dessus, procédez comme suit:

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

Si vous devez cibler des navigateurs qui ne prennent pas en charge ES6, vous pouvez exécuter le code par le biais de Babel ou de fermeture-compilateur pour générer ECMAScript 5.

Les rappels ...argssont encapsulés dans un tableau et déstructurés lorsque vous les lisez afin que le modèle puisse gérer des rappels comportant plusieurs arguments. Par exemple avec le noeud fs :

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

En utilisant ES2017, vous devriez avoir ceci comme déclaration de fonction

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

Et l'exécuter comme ça.

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

Ou la syntaxe Promise

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})

La question était:

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

qui PEUVENT être interprétés comme:

Comment rendre le code asynchrone synchrone ?

La solution consistera à éviter les rappels et à utiliser une combinaison de Promises et async / wait .

Je voudrais donner un exemple pour une demande Ajax.

(Bien qu'il puisse être écrit en Javascript, je préfère l'écrire en Python et le compiler en Javascript avec Transcrypt . Ce sera assez clair.)

Permet d'abord d'activer l'utilisation de JQuery, pour qu'il soit $disponible en tant que S:

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

Définissez une fonction qui retourne une promesse , en l'occurrence un appel Ajax:

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

Utilisez le code asynchrone comme s'il était synchrone :

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

Une autre approche pour renvoyer une valeur d'une fonction asynchrone consiste à transmettre un objet qui stockera le résultat de la fonction asynchrone.

Voici un exemple du même:

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

J'utilise l' resultobjet pour stocker la valeur pendant l'opération asynchrone. Cela permet au résultat d'être disponible même après le travail asynchrone.

J'utilise beaucoup cette approche. Je serais intéressé de savoir dans quelle mesure cette approche fonctionne dans les cas où le résultat est renvoyé par le biais de modules consécutifs


Bien sûr, il existe de nombreuses approches telles que la demande synchrone, c'est prometteur, mais de mon expérience, je pense que vous devriez utiliser l'approche de rappel. C'est naturel au comportement asynchrone de Javascript. Ainsi, votre extrait de code peut être réécrit un peu différemment:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

C’est l’un des endroits où la liaison de données utilisée de nombreux nouveaux frameworks JavaScript fonctionnera grandement pour vous ...

Donc, si vous utilisez Angular, React ou tout autre framework qui lie la liaison de données de deux manières, ce problème est simplement résolu pour vous. Ainsi, votre résultat est undefinedà la première étape result = undefined. ensuite, dès que vous obtenez le résultat, il sera mis à jour et attribué à la nouvelle valeur qui correspond à votre appel Ajax ...

Mais comment faire en javascript pur ou en jQuery, par exemple, comme vous l'avez demandé dans cette question?

Vous pouvez utiliser un rappel , promesse et récemment observable à gérer pour vous, par exemple dans les promesses que nous avons une fonction comme le succès () ou alors () qui sera exécuté lorsque vos données est prêt pour vous, même avec rappel ou s'abonner fonction sur observable .

Par exemple, dans votre cas où vous utilisez jQuery , vous pouvez faire quelque chose comme ceci:

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

Pour plus d’informations, étudiez les promesses et les observables qui sont de nouvelles façons de faire cela.


Je vais répondre avec une bande dessinée horrible, dessinée à la main. La deuxième image est la raison pour laquelle resultest undefineddans votre exemple de code.


La plupart des réponses ici donnent des suggestions utiles lorsque vous avez une seule opération async, mais parfois, cela se produit lorsque vous devez effectuer une opération asynchrone pour chaque entrée d'un tableau ou d'une autre structure semblable à une liste. La tentation est de faire ceci:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., 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); // E.g., 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;
}

Cela ne fonctionne pas parce que les rappels doSomethingAsyncn'ont pas encore été exécutés au moment où vous essayez d'utiliser les résultats.

Ainsi, si vous avez un tableau (ou une liste quelconque) et que vous souhaitez effectuer des opérations asynchrones pour chaque entrée, vous avez deux options: Effectuez les opérations en parallèle (se chevauchant) ou en série (une après l'autre en séquence).

Parallèle

Vous pouvez tous les démarrer et noter le nombre de rappels que vous attendez, puis utiliser les résultats lorsque vous avez reçu autant de rappels:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

Exemple:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

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

(Nous pourrions nous en débarrasser expectinget simplement nous en servir results.length === theArray.length, mais cela nous laisse ouverts à la possibilité de theArraychanger pendant que les appels sont en attente ...)

Remarquez comment nous utilisons l'option indexfrom forEachpour enregistrer le résultat dans resultsla même position que l'entrée à laquelle elle se rapporte, même si les résultats arrivent dans le désordre (étant donné que les appels asynchrones ne se terminent pas nécessairement dans l'ordre dans lequel ils ont été démarrés).

Mais que se passe-t-il si vous devez renvoyer les résultats d'une fonction? Comme les autres réponses l'ont souligné, vous ne pouvez pas; votre fonction doit accepter et rappeler un rappel (ou renvoyer une then() ). Voici une version de rappel:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

Exemple:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

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

Ou voici une version renvoyant un à la Promiseplace:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Bien sûr, si doSomethingAsyncnous passions des erreurs, nous utilisions rejectpour rejeter la promesse quand nous avions une erreur.)

Exemple:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

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

(Ou alternativement, vous pourriez faire une enveloppe pour doSomethingAsyncque cela retourne une promesse, et ensuite faire le dessous ...)

Si doSomethingAsyncvous vous then() , vous pouvez utiliser Promise.all:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Exemple:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Notez que cela Promise.allrésout sa promesse avec un tableau des résultats de toutes les promesses que vous lui faites quand elles sont toutes résolues, ou rejette sa promesse lorsque la première des promesses que vous lui faites est rejetée.

Séries

Supposons que vous ne vouliez pas que les opérations soient en parallèle? Si vous souhaitez les exécuter les unes après les autres, vous devez attendre la fin de chaque opération avant de commencer la suivante. Voici un exemple de fonction qui fait cela et appelle un rappel avec le résultat:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(Puisque nous travaillons en série, nous pouvons simplement utiliser results.push(result)car nous savons que les résultats ne seront pas erronés. Dans ce qui précède, nous aurions pu utiliser results[index] = result;, mais dans certains des exemples suivants, nous n'avons pas d'index. utiliser.)

Exemple:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

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

(Ou encore, construire un emballage pour doSomethingAsynccela vous donne une promesse et faire le ci-dessous ...)

Si doSomethingAsyncvous faites une promesse, si vous pouvez utiliser la syntaxe ES2017 + (peut-être avec un transpiler comme Babel ), vous pouvez utiliser une async/await avec for-ofet await:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Exemple:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Si vous ne pouvez pas (encore) utiliser la syntaxe ES2017 +, vous pouvez utiliser une variante du motif "Réduire la promesse" (cette opération est plus complexe que la réduction habituelle de Promise, car nous ne transmettons pas le résultat de l'un à l'autre, mais plutôt rassemblant leurs résultats dans un tableau):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Exemple:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

... qui est moins encombrant avec les fonctions ES2015 + flèche :

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Exemple:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}


La solution la plus simple consiste à créer une fonction JavaScript et à l'appeler pour le successrappel Ajax .

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

Nous nous trouvons dans un univers qui semble progresser selon une dimension appelée "temps". Nous ne comprenons pas vraiment ce qu'est le temps, mais nous avons développé des abstractions et un vocabulaire qui permettent de raisonner et d'en parler: "passé", "présent", "futur", "avant", "après".

Les systèmes informatiques que nous construisons - de plus en plus - ont le temps comme dimension importante. Certaines choses sont prêtes pour l'avenir. Ensuite, il faut que d'autres choses se produisent après que ces premières choses se sont finalement produites. C'est la notion de base appelée "asynchronicité". Dans notre monde de plus en plus en réseau, le cas le plus courant d'asynchonicité attend qu'un système distant réponde à une requête.

Prenons un exemple. Vous appelez le laitier et commandez du lait. Quand ça vient, tu veux le mettre dans ton café. Vous ne pouvez pas mettre le lait dans votre café pour l'instant, car il n'est pas encore arrivé. Vous devez attendre qu'il vienne avant de le mettre dans votre café. En d'autres termes, les éléments suivants ne fonctionneront pas:

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

Parce que JS n'a aucun moyen de savoir qu'il doit attendre pour order_milkfinir avant qu'il exécute put_in_coffee. En d’autres termes, elle ne sait pas qu’elle order_milkest asynchrone - c’est quelque chose qui ne produira pas de lait avant un certain temps. JS et d'autres langages déclaratifs exécutent une instruction après l'autre sans attendre.

L’approche classique de ce problème par JS, tirant parti du fait que JS prend en charge les fonctions en tant qu’objets de première classe pouvant être échangés, consiste à transmettre une fonction en tant que paramètre à la requête asynchrone, qu’elle invoquera ensuite une fois terminée. sa tâche dans le futur. C'est l'approche de "rappel". Cela ressemble à ceci:

order_milk(put_in_coffee);

order_milkcommence, commande le lait, alors, quand et seulement quand il arrive, il invoque put_in_coffee.

Le problème avec cette approche de rappel est qu’elle pollue la sémantique normale d’une fonction rapportant son résultat avec return; à la place, les fonctions ne doivent pas signaler leurs résultats en appelant un rappel donné en tant que paramètre. En outre, cette approche peut rapidement devenir lourde s’agissant de séquences d’événements plus longues. Par exemple, supposons que je veuille attendre que le lait soit versé dans le café, puis seulement à ce moment-là, effectue une troisième étape, à savoir boire le café. Je finis par avoir besoin d'écrire quelque chose comme ceci:

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

où je passe à la put_in_coffeefois le lait à y mettre, ainsi que l'action ( drink_coffee) à exécuter une fois que le lait a été introduit. Ce code devient difficile à écrire, à lire et à corriger.

Dans ce cas, nous pourrions réécrire le code dans la question comme suit:

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

function callback(data) {
  console.log(data);
}

Entrer des promesses

C'était la motivation de la notion de "promesse", qui est un type particulier de valeur qui représente un résultat futur ou asynchrone . Cela peut représenter quelque chose qui est déjà arrivé ou qui va arriver dans le futur ou peut ne jamais arriver du tout. Les promesses ont une seule méthode, nommée then, à laquelle vous transmettez une action à exécuter lorsque le résultat représenté par la promesse est atteint.

Dans le cas de notre lait et de notre café, nous concevons order_milkde retourner une promesse pour le lait qui arrive, puis spécifions put_in_coffeecomme une thenaction, comme suit:

order_milk() . then(put_in_coffee)

Un avantage de ceci est que nous pouvons les chaîner ensemble pour créer des séquences d'occurrences futures ("chaînage"):

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

Appliquons des promesses à votre problème particulier. Nous allons envelopper notre logique de requête dans une fonction, qui retourne une promesse:

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

En fait, tout ce que nous avons fait est ajouté returnà l'appel $.ajax. Cela fonctionne car jQuery $.ajaxrenvoie déjà une sorte de chose qui ressemble à une promesse. (En pratique, sans entrer dans les détails, nous préférerions conclure cet appel afin de renvoyer une vraie promesse, ou utiliser une alternative à $.ajaxcela.) Maintenant, si nous voulons charger le fichier et attendre qu'il se termine, puis faire quelque chose, on peut simplement dire

get_data() . then(do_something)

par exemple,

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

Lorsque nous utilisons des promesses, nous thenfinissons par introduire beaucoup de fonctions . Il est donc souvent utile d’utiliser les fonctions de flèche plus compactes du style ES6:

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

Le asyncmot clé

Mais il y a toujours quelque chose de vaguement insatisfaisant à propos de l'écriture de code d'une manière si synchrone et d'une manière tout à fait différente si asynchrone. Pour synchrone, on écrit

a();
b();

mais si aest asynchrone, avec des promesses nous devons écrire

a() . then(b);

Ci-dessus, nous avons dit "JS n'a aucun moyen de savoir qu'il doit attendre la fin du premier appel avant d'exécuter le second". Ce ne serait pas bien s'il y avait un moyen de dire cela à JS? Il s’avère que le awaitmot - clé est utilisé dans un type spécial de fonction appelé fonction "asynchrone". Cette fonctionnalité fait partie de la version à venir d'ES, mais est déjà disponible dans les transpilers tels que Babel avec les bons presets. Cela nous permet d'écrire simplement

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

Dans votre cas, vous seriez capable d’écrire quelque chose comme:

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

Une autre solution consiste à exécuter du code via l'exécuteur séquentiel nsynjs .

Si la fonction sous-jacente est promise

nsynjs évaluera toutes les promesses de manière séquentielle et mettra le résultat de la promesse en datapropriété:

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');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

Si la fonction sous-jacente n'est pas promise

Étape 1. Enroulez la fonction avec le rappel dans l'encapsuleur nsynjs (si elle a une version promise, vous pouvez ignorer ce test):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Étape 2. Mettez la logique synchrone en fonction:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Étape 3. Exécutez la fonction de manière synchrone via nnsynjs:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs évaluera tous les opérateurs et expressions étape par étape, suspendant l'exécution au cas où le résultat d'une fonction lente n'est pas prêt.

Plus d'exemples ici: https://github.com/amaksr/nsynjs/tree/master/examples


Utilisez une callback()fonction dans le foo()succès. Essayez de cette façon. C'est simple et facile à comprendre.

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-2017