Comment attendre la fin d'une promesse avant de renvoyer la variable d'une fonction?

149

J'ai encore du mal avec les promesses, mais je fais des progrès grâce à la communauté ici.

J'ai une fonction JS simple qui interroge une base de données Parse. Il est censé renvoyer le tableau des résultats, mais évidemment en raison de la nature asynchrone de la requête (d'où les promesses), la fonction retourne avant les résultats, me laissant avec un tableau non défini.

Que dois-je faire pour que cette fonction attende le résultat de la promesse?

Voici mon code:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    var promise = query.find({
               success: function(results) {
               // results is an array of Parse.Object.
                             console.log(results);
                             //resultsArray = results;
                             return results;
               },

               error: function(error) {
               // error is an instance of Parse.Error.
                             console.log("Error");
               }
    });                           

}
mac_55
la source
3
Vous pouvez également envisager d'utiliser async / await. Node prend désormais en charge async / await hors de la boîte depuis la version 7.6
Viliam Simko

Réponses:

66

Au lieu de renvoyer un, resultsArrayvous renvoyez une promesse pour un tableau de résultats, puis thensur le site d'appel - cela présente l'avantage supplémentaire de savoir que l'appelant sait que la fonction effectue des E / S asynchrones. Le codage de la concurrence en JavaScript est basé sur cela - vous voudrez peut-être lire cette question pour avoir une idée plus large:

function resultsByName(name)
{   
    var Card = Parse.Object.extend("Card");
    var query = new Parse.Query(Card);
    query.equalTo("name", name.toString());

    var resultsArray = [];

    return query.find({});                           

}

// later
resultsByName("Some Name").then(function(results){
    // access results here by chaining to the returned promise
});

Vous pouvez voir plus d'exemples d'utilisation de promesses d'analyse avec des requêtes dans le propre blog de Parse à ce sujet .

Benjamin Gruenbaum
la source
pouvez-vous me dire quel est le soutien de cela? IE9 prend en charge cela?
sandrina-p
Oui, mais Parse lui-même est presque mort, il y a donc @SandrinaPereira. Il s'agit d'analyser le code du cloud .
Benjamin Gruenbaum
1
Ah donc ce n'est pas seulement du javascript pur? Je cherchais un moyen de faire cela (attendez qu'une fonction se termine pour en démarrer une autre) mais uniquement avec du javascript pur ..
sandrina-p
La question concerne le code d'analyse, pas les promesses. Les promesses peuvent fonctionner (avec une bibliothèque) dans n'importe quel navigateur. Bluebird fonctionne dans IE6 et netscape 7.
Benjamin Gruenbaum
1
Je lis SO depuis deux jours maintenant, et personne n'a encore résolu cela. Cette réponse acceptée est la même que toutes les autres. La fonction renvoie une promesse, pas une valeur comme l'OP demandée. Pourquoi cette réponse est-elle marquée comme acceptée?
iGanja
19

Que dois-je faire pour que cette fonction attende le résultat de la promesse?

Utiliser async/await(NE FAIT PAS partie d'ECMA6, mais disponible pour Chrome, Edge, Firefox et Safari depuis fin 2017, voir canIuse )
MDN

    async function waitForPromise() {
        // let result = await any Promise, like:
        let result = await Promise.resolve('this is a sample promise');
    }

Ajouté en raison d'un commentaire: une fonction asynchrone renvoie toujours une promesse, et dans TypeScript, cela ressemblerait à:

    async function waitForPromise(): Promise<string> {
        // let result = await any Promise, like:
        let result = await Promise.resolve('this is a sample promise');
    }
Martin Meeser
la source
4
La fonction asynchrone renverra toujours un objet de promesse si elle est appelée sans attente (ou en code non asynchrone). Vérifiez le résultat de console.log (waitForPromise ()) si vous n'êtes pas sûr. Une vérification de console.log (résultat) dans la fonction async affichera ce que vous attendez, mais le retour de la fonction async se produit immédiatement sans blocage et renvoie une promesse. Le blocage en javascript est généralement très mauvais car il s'agit d'une application à thread unique et le blocage affamera tous les autres clients pub / sous de notifications, ce qui amènera essentiellement l'application entière à genoux.
SRM
1
.net a .wait () sur la classe de tâches "promise". Cette fonctionnalité manque-t-elle à Javascript? Je dois attendre quelque chose avant de quitter mon outil de ligne de commande de nœud qui peut diriger sa sortie vers un autre outil. "Wait" ne fonctionne que dans les fonctions asynchrones. Cela signifie que cela ne fonctionne pas en dehors du cadre d'une promesse.
TamusJRoyce
@SRM J'ai l'impression que votre commentaire est basé sur une mauvaise interprétation de l'exemple - il s'agit du Promise.resolve "interne" (comme l'exemple le plus simple de Promise) donc il n'y a pas d'appelant externe comme vous le dites dans votre commentaire. J'ai donc décidé de mettre à jour la réponse.
Martin Meeser
@TamusJRoyce Je suppose que c'est une question en soi, mais je pense qu'en C # vous pouvez utiliser Task.ContinueWith (Task), qui est la même idée que celle que vous voyez dans la réponse acceptée (où elle s'appelle "then ()").
Martin Meeser
Je pense que je vois maintenant. Je peux à peu près envelopper tout mon script dans une énorme fonction asynchrone. Et appelez cette fonction comme la dernière ligne de mon script. Cette fonction serait encore un peu de plaque chauffante. Mais beaucoup moins que ce que j'avais perçu auparavant. Je n'ai pas l'habitude d'écrire des fonctions dans des fonctions. Merci @MartinMeeser!
TamusJRoyce
3

Vous ne voulez pas faire attendre la fonction, car JavaScript est destiné à être non bloquant. Renvoyez plutôt la promesse à la fin de la fonction, puis la fonction appelante peut utiliser la promesse pour obtenir la réponse du serveur.

var promise = query.find(); 
return promise; 

//Or return query.find(); 
Trace
la source
1
Tout votre truc de rappels avec le success:bit est désactivé.
Benjamin Gruenbaum
Ou mieux: return query.find();.
écraser le
Aussi correct. Je vais juste le laisser comme ça à des fins d'illustration, mais je l'ai ajouté en commentaire.
Trace le
J'ai essayé ceci mais les résultats semblent indéfinis. resultsByName ("nom"). then (fonction (résultats) {console.log ("tableau obtenu" + nombre de résultats);});
mac_55
1
Merci, il doit y avoir une sorte d'erreur dans la fonction de résultats. Ça marche maintenant. J'ai changé mon console.log en results.length et je peux voir qu'il y a 1 entrée dans mon tableau retourné :)
mac_55
2

Vous n'utilisez pas réellement de promesses ici. Parse vous permet d'utiliser des rappels ou des promesses; votre choix.

Pour utiliser les promesses, procédez comme suit:

query.find().then(function() {
    console.log("success!");
}, function() {
    console.log("error");
});

Maintenant, pour exécuter des choses une fois la promesse terminée, vous pouvez simplement l'exécuter dans le rappel de promesse à l'intérieur de l' then()appel. Jusqu'à présent, ce serait exactement la même chose que les rappels réguliers.

Pour faire bon usage des promesses, c'est quand vous les enchaînez, comme ceci:

query.find().then(function() {
    console.log("success!");

    return new Parse.Query(Obj).get("sOmE_oBjEcT");
}, function() {
    console.log("error");
}).then(function() {
    console.log("success on second callback!");
}, function() {
    console.log("error on second callback");
});
purée
la source
Quel type d'objet est l'objet de résultats? car il ne semble pas contenir mon tableau
mac_55
Ce devrait être un tableau de Parse.Object's.
écraser le