Maison > Questions et réponses > le corps du texte
Comment renvoyer une réponse/résultat d'une fonction foo
qui fait une requête asynchrone ?
J'ai essayé de renvoyer la valeur du rappel et d'attribuer le résultat à une variable locale à l'intérieur de la fonction et de renvoyer cette variable, mais aucune de ces méthodes ne renvoie réellement de réponse - elles renvoient toutes undefined
或其他变量 result
avec la valeur initiale de .
Exemple de fonction asynchrone qui accepte un rappel (en utilisant la fonction ajax
de jQuery) :
function foo() { var result; $.ajax({ url: '...', success: function(response) { result = response; // return response; // <- I tried that one as well } }); return result; // It always returns `undefined` }
Exemple utilisant Node.js :
function foo() { var result; fs.readFile("path/to/file", function(err, data) { result = data; // return data; // <- I tried that one as well }); return result; // It always returns `undefined` }
Exemple d'utilisation du bloc de promesse then
:
function foo() { var result; fetch(url).then(function(response) { result = response; // return response; // <- I tried that one as well }); return result; // It always returns `undefined` }
P粉4773692692023-10-09 09:40:11
Votre code devrait ressembler à 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 fait un excellent travailen écrivant la réponse pour les personnes utilisant jQuery pour AJAX, mais j'ai décidé de proposer une alternative aux personnes qui n'utilisent pas jQuery.
Voici un bref résumé de "l'explication de la question" d'une autre réponse, si vous n'êtes pas sûr après avoir lu ceci, veuillez lire cette réponse.
LeA dans AJAX signifie asynchrone. Cela signifie que l'envoi de la requête (ou plutôt la réception de la réponse) est supprimé du flux normal d'exécution. Dans votre exemple, .send code>
立即返回,并且在调用您作为 .send code>
success
回调传递的函数之前执行下一条语句 return result;
renvoie immédiatement et l'instruction suivante return result;
est exécutée avant d'appeler la fonction que vous avez passée comme rappel success
.code>.
Cela signifie que lorsque vous revenez, l'écouteur que vous avez défini n'a pas encore été exécuté, ce qui signifie que la valeur que vous avez renvoyée n'a pas encore été définie.
Voici une analogie simple :
function getFive(){ var a; setTimeout(function(){ a=5; },10); return a; }
Grâce à a=5
部分尚未执行,因此返回的 a
值为 undefined
. AJAX se comporte de telle manière que vous renvoyez la valeur avant que le serveur n'ait la possibilité d'indiquer à votre navigateur quelle est la valeur.
Une solution possible à ce problème consiste à réactivement écrire du code qui indique à votre programme quoi faire une fois le calcul terminé.
function onComplete(a){ // When the code completes, do this alert(a); } function getFive(whenDone){ var a; setTimeout(function(){ a=5; whenDone(a); },10); }
Cela s'appelle CPS. Fondamentalement, nous transmettons getFive
une action à effectuer une fois terminée, et nous indiquons à notre code comment réagir lorsque l'événement se termine (comme notre appel AJAX, ou dans ce cas, un délai d'attente). < /p>
L'utilisation est :
getFive(onComplete);
"5" apparaîtra à l'écran. (violon).
Il existe essentiellement deux manières de résoudre ce problème :
Quant à l'AJAX synchrone, Ne le faites pas ! La réponse de Félix présente des arguments convaincants expliquant pourquoi c'est une mauvaise idée. Dans l'ensemble, cela gèle le navigateur de l'utilisateur jusqu'à ce que le serveur renvoie une réponse et crée une très mauvaise expérience utilisateur. Voici un autre bref résumé de MDN expliquant pourquoi :
Si vous devez faire cela, vous pouvez passer un drapeau. La méthode spécifique est la suivante :
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); }
Faites en sorte que votre fonction accepte les rappels. Dans l'exemple de code, vous pouvez savoir comment foo
接受回调。我们将告诉代码当 foo
réagit lorsque est terminé.
Donc :
var result = foo(); // Code that depends on `result` goes here
devient :
foo(function(result) { // Code that depends on `result` });
Ici, nous transmettons une fonction anonyme, mais nous pourrions tout aussi bien transmettre une référence à une fonction existante, la faisant ressembler à :
function myHandler(result) { // Code that depends on `result` } foo(myHandler);
Pour plus de détails sur la façon de réaliser ce type de conception de rappel, consultez la réponse de Félix.
Maintenant, définissons foo lui-même pour fonctionner 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(); }
Nous avons maintenant la fonction foo qui accepte une action à exécuter lorsque AJAX se termine avec succès. Nous pouvons étendre davantage cette fonctionnalité en vérifiant si l'état de la réponse n'est pas 200 et en prenant les mesures appropriées (création d'un gestionnaire d'échec, etc.). Cela a effectivement résolu notre problème.
Si vous avez toujours du mal à comprendre cela, Lisez AJAX pour un guide pour démarrer sur MDN.
P粉5150665182023-10-09 00:20:22
A dans Ajax signifie asynchrone. Cela signifie que l'envoi de la requête (ou plutôt la réception de la réponse) est supprimé du flux normal d'exécution. Dans votre exemple, $.ajax
立即返回,并且下一条语句 return result;
在您作为 success
le rappel est passé avant même que la fonction ne soit appelée.
Voici une analogie qui, espérons-le, rendra plus claire la différence entre les flux synchrones et asynchrones :
Imaginez que vous appeliez un ami et lui demandiez de trouver des informations pour vous. Même si cela peut prendre un certain temps, vous attendez près du téléphone, le regard dans le vide, jusqu'à ce que votre ami vous donne la réponse dont vous avez besoin.
La même chose se produit lorsque vous effectuez un appel de fonction contenant du code "normal":
function findItem() { var item; while(item_not_found) { // search } return item; } var item = findItem(); // Do something with item doSomethingElse();
Bien que tout code après findItem
可能需要很长时间才能执行,但 var item = findItem();
doive attendre jusqu'à ce que la fonction renvoie le résultat.
Vous appelez à nouveau votre ami pour la même raison. Mais cette fois, vous lui dites que vous êtes anxieux et qu'il devrait vous rappeler en utilisant 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 les informations qu'il vous a fournies.
C'est exactement ce qui se passe lorsque vous faites une requête Ajax.
findItem(function(item) { // Do something with the item }); doSomethingElse();
N'attend pas de réponse, mais continue l'exécution immédiatement et exécute l'instruction après l'appel Ajax. Afin d'obtenir enfin la réponse, vous devez fournir une fonction qui est appelée après la réception de la réponse, un rappel (vous avez remarqué quelque chose ? Un rappel ?). Toutes les instructions après cet appel seront exécutées avant que le rappel ne soit appelé.
Adoptez la nature asynchrone de JavaScript ! Bien que certaines opérations asynchrones fournissent des contreparties synchrones (comme le fait "Ajax"), leur utilisation est généralement déconseillée, notamment dans un contexte de navigateur.
Pourquoi est-ce mauvais, demandez-vous ?
JavaScript s'exécute dans le thread de l'interface utilisateur du navigateur et tout processus de longue durée peut verrouiller l'interface utilisateur, la rendant insensible. De plus, il existe une limite supérieure au temps d'exécution de JavaScript et le navigateur demandera à l'utilisateur s'il doit poursuivre l'exécution.
Tout cela conduit à une très mauvaise expérience utilisateur. L'utilisateur ne pourra pas savoir si tout fonctionne correctement. De plus, l’effet sera pire pour les utilisateurs dont la vitesse Internet est plus lente.
Nous présentons ci-dessous trois solutions différentes qui s'appuient toutes les unes sur les autres :
async/await
(ES2017+, fonctionne dans les anciens navigateurs si vous utilisez un transpileur ou un régénérateur) then() 的 Promise
(ES2015+, fonctionne dans les anciens navigateurs si vous utilisez l'une des nombreuses bibliothèques Promise) Ces trois fonctionnalités sont disponibles dans les navigateurs actuels et Node 7+.
async/await 进行承诺
La version 2017 d'ECMAScript a introduit la prise en charge au niveau de la syntaxe pour les fonctions asynchrones. Avec async
和await
vous pouvez écrire de manière asynchrone dans un "style synchrone". Le code est toujours asynchrone, mais plus facile à lire/comprendre.
async/await
构建在 Promise 之上:async
函数始终返回 Promise。 await
Construit sur une promesse : les fonctions
await "déverrouille" une promesse et produit soit la valeur à laquelle la promesse est résolue, soit génère une erreur si la promesse est rejetée.
async
函数或 JavaScript 模块。模块外部不支持顶级 await
,因此您可能必须创建异步 IIFE (立即调用函数表达式)来启动异步
IMPORTANT
await que dans les fonctions async ou Modules JavaScript. Le niveau supérieur <的更多信息/code>await
n'est pas pris en charge en dehors du module, vous devrez donc peut-être créer un IIFE asynchrone (Expression de fonction immédiatement invoquée ) pour démarrer le contexte async (si vous n'utilisez pas de modules).
< MDN 上的 /code>Vous pouvez en savoir plus sur
et findItem()
await
.
Voici un exemple détaillant la fonction delayed async/await
findItem() 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 superagent.get('/books/ids='+JSON.stringify(bookIDs));
} catch(error) {
// If any of the awaited promises was rejected, this catch block
// would catch the rejection reason
return null;
}
}
// Start an IIFE to use `await` at the top level
(async function(){
let books = await getAllBooks();
console.log(books);
})();
Les versions actuelles de browser et
Babel
). 🎜 🎜 🎜Laissez la fonction accepter les rappels🎜🎜 🎜Le rappel a lieu lorsque la fonction 1 est transmise à la fonction 2. La fonction 2 peut appeler la fonction 1 lorsqu'elle est prête. Dans le contexte d'un processus asynchrone, le rappel est appelé chaque fois que le processus asynchrone se termine. Normalement, les résultats sont transmis au rappel. 🎜Dans l'exemple de la question, vous pouvez effectuer foo
接受回调并将其用作 success
un rappel. Alors ça
var result = foo(); // Code that depends on 'result'
est devenu
foo(function(result) { // Code that depends on 'result' });
Ici, nous définissons une 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 à laquelle nous avons passé callback
将引用我们调用时传递给 foo
的函数,并将其传递给 success
。 IE。一旦Ajax请求成功,$.ajax
将调用callback
并将响应传递给回调(可以用result
lorsque nous l'avons appelée, et la transmettra à success
. c'est à dire. Une fois la requête Ajax réussie, $.ajax
appellera callback
et transmettra la réponse au rappel (qui peut être référencée avec result
, puisque c'est ainsi que nous définissons le rappel (The way).
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); } }); }
Écrire du code à l'aide de rappels est plus facile qu'il n'y paraît. Après tout, JavaScript dans les navigateurs est en grande partie piloté par des événements (événements DOM). Recevoir une réponse Ajax n'est rien de plus qu'un événement. Des difficultés peuvent survenir lorsque vous devez utiliser du code tiers, mais la plupart des problèmes peuvent être résolus en pensant simplement au flux de l'application.
Promise API est une nouvelle fonctionnalité ECMAScript 6 (ES2015), mais elle dispose déjà d'un bon support de navigateur. Il existe également de nombreuses bibliothèques qui implémentent l'API Promises standard et fournissent des méthodes supplémentaires pour simplifier l'utilisation et la composition des fonctions asynchrones (par exemple, Bluebird).
La promesse est un conteneur de valeurs futures. Lorsqu'une promesse reçoit une valeur (résolue) ou est annulée (rejetée), elle avertit tous les « auditeurs » qui souhaitent accéder à la valeur. < /p>
L'avantage par rapport aux rappels normaux est qu'ils vous permettent de découpler votre code et sont plus faciles à écrire.
Voici un exemple d'utilisation de 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);
});
}
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).
});
.as-console-wrapper { max-height: 100% !important; top: 0; }