Comment vérifier si une promesse angulaire $ q est résolue

84

Je comprends qu'en général, on attache simplement du code de continuation avec un then()comportement d'appel et de chaîne lors de l'utilisation de promesses.

Cependant, je veux lancer un appel asynchrone enveloppé de promesse, puis lancer séparément un 3 secondes $timeout()afin de pouvoir effectuer une action de l'interface utilisateur, UNIQUEMENT SI la promesse d'origine n'est pas encore terminée. (Je prévois que cela ne se produirait que sur les connexions lentes, les appareils mobiles en 3G, etc.)

Étant donné une promesse, puis-je vérifier si elle est complète ou non sans blocage ou attente?

blaster
la source
2
J'ai ouvert un problème à ce sujet en angular et j'ai obtenu une réponse utile github.com/angular/angular.js/issues/8307#issuecomment-49903373
derekdreery
duplication possible de stackoverflow.com/questions/27039771/…
mvermand

Réponses:

46

Je suppose que cela a été ajouté dans une version récente d'Angular mais il semble y avoir maintenant un objet $$ state sur la promesse:

 var deferred = $q.defer();
 console.log(deferred.promise.$$state.status); // 0
 deferred.resolve();
 console.log(deferred.promise.$$state.status); //1 

Comme indiqué dans les commentaires, cela n'est pas recommandé car cela pourrait casser lors de la mise à niveau de votre version Angular.

parlement
la source
25
Les documents angulaires indiquent que les $$...propriétés ne doivent pas être utilisées. Peut être risqué lors de la mise à niveau vers des versions plus récentes d'Angular ...
hgoebl
7
Dommage qu'Angular fournit ses variables privées sur un plateau d'argent. :(
Jackson
2
Mais mais ... Angular n'a pas implémenté la inspectfonction. Donc pas de jus pour nous les développeurs angulaires. Promise prototype ne l'a tout simplement pas.
Robert Koritnik
36

Je pense que votre meilleure option telle quelle (sans modifier la source angulaire et soumettre une demande de tirage) est de conserver un drapeau local pour savoir si la promesse a été résolue. Réinitialisez-le chaque fois que vous configurez la promesse qui vous intéresse et marquez-la comme terminée dans le then()pour la promesse d'origine. Dans le $timeout then()cochez le drapeau pour savoir si la promesse originale est encore résolue ou non.

Quelque chose comme ça:

var promiseCompleted = false;
promise.then(function(){promiseCompleted=true;})
$timeout(...).then(function(){if(!promiseCompleted)doStuff()})

L'implémentation de Kris Kowal inclut d'autres méthodes pour vérifier l'état de la promesse, mais il semble que l'implémentation d'Angular $qne les inclut malheureusement pas.

shaunhusain
la source
9
Il serait préférable d'utiliser .finally () ici. Le code fourni ci-dessus marquera uniquement l'indicateur promiseCompleted sur true s'il a été résolu avec succès.
Karanvir Kang
8

Cela ne semble pas possible, comme @shaunhusain l'a déjà mentionné. Mais peut-être que ce n'est pas nécessaire:

// shows stuff from 3s ahead to promise completetion, 
// or does and undoes it in one step if promise completes before
$q.all(promise, $timeout(doStuff, 3000)).then(undoStuff);

ou peut-être mieux:

var tooSlow = $timeout(doStuff, 3000);
promise.always(tooSlow.cancel);
Bergi
la source
1

J'ai eu un problème similaire où je dois vérifier si une promesse est revenue. Étant donné que la $watchfonction d'AngularJS enregistrera un changement lors du rendu de la page même si les valeurs nouvelles et anciennes ne sont pas définies, je dois vérifier s'il existe des données qui valent la peine d'être stockées dans mon modèle externe.

C'est définitivement un hack mais je fais ceci:

$scope.$watch('userSelection', function() {
  if(promiseObject.hasOwnProperty("$$v"){
    userExportableState.selection = $scope.userSelection;
  }
};

Je sais que $$vc'est une variable interne utilisée par AngularJS, mais cela a été assez fiable comme indicateur d'une promesse résolue pour nous. Qui sait ce qui se passera lorsque nous passerons à AngularJS 1.2: - / Je ne vois aucune mention d'améliorations $qdans la documentation 1.2, mais peut-être que quelqu'un écrira un service de remplacement avec une meilleure fonctionnalité définie plus près de Q.

PatchCR
la source
Merci, mais il y a de meilleures options que d'utiliser des membres "privés".
Jackson du
0

Je ne connais pas votre scénario exact, mais il est plus courant de mettre en place un délai d'attente immédiatement après avoir effectué l'appel asynchrone (et généré la promesse).

Si l' setTimeout()instruction se trouve dans le même thread d'événement que l'appel asynchrone, vous n'avez pas à vous soucier de la possibilité d'un effet de course. Comme javascript est strictement à thread unique, les .then()rappels de la promesse sont garantis de se déclencher dans un thread d'événement ultérieur.

Betterave-Betterave
la source