Utilisation réussie / error / finally / catch avec Promises dans AngularJS

J’utilise $http dans AngularJs et je ne sais pas comment utiliser la promesse renvoyée et gérer les erreurs.

J’ai ce code:

 $http .get(url) .success(function(data) { // Handle data }) .error(function(data, status) { // Handle HTTP error }) .finally(function() { // Execute logic independent of success/error }) .catch(function(error) { // Catch and handle exceptions from success/error/finally functions }); 

Est-ce un bon moyen de le faire ou existe-t-il un moyen plus facile?

Les promesses sont une abstraction des déclarations qui nous permettent de nous exprimer de manière synchrone avec le code asynchrone. Ils représentent une exécution d’une tâche ponctuelle.

Ils fournissent également la gestion des exceptions, tout comme le code normal, vous pouvez revenir d’une promesse ou vous pouvez lancer.

Ce que vous voulez en code synchrone est:

 try{ try{ var res = $http.getSync("url"); res = someProcessingOf(res); } catch (e) { console.log("Got an error!",e); throw e; // rethrow to not marked as handled } // do more stuff with res } catch (e){ // handle errors in processing or in error. } 

La version promis est très similaire:

 $http.get("url"). then(someProcessingOf). catch(function(e){ console.log("got an error in initial processing",e); throw e; // rethrow to not marked as handled, // in $q it's better to `return $q.reject(e)` here }).then(function(res){ // do more stuff }).catch(function(e){ // handle errors in processing or in error. }); 

Ne plus utiliser error méthode des success et des error .

Les deux méthodes ont été désapprouvées dans angular 1.4. Fondamentalement, la raison de cette déchéance est qu’ils ne sont pas compatibles avec la chaîne , pour ainsi dire.

Avec l’exemple suivant, je vais essayer de démontrer ce que je veux dire à propos de la success et de l’ error qui ne sont pas compatibles avec la chaîne . Supposons que nous appelions une API qui renvoie un object utilisateur avec une adresse:

Objet utilisateur:

 {name: 'Igor', address: 'San Francisco'} 

Appel à l’API:

 $http.get('/user') .success(function (user) { return user.address; <--- }) | // you might expect that 'obj' is equal to the .then(function (obj) { ------ // address of the user, but it is NOT console.log(obj); // -> {name: 'Igor', address: 'San Francisco'} }); }; 

Qu’est-il arrivé?

Le success et l’ error $http.get la promesse d’origine , c’est-à-dire celle renvoyée par $http.get , l’object passé au callback de l’ensemble est l’object utilisateur complet, c’est-à-dire la même entrée que le rappel précédent.

Si nous avions enchaîné deux, cela aurait été moins déroutant:

 $http.get('/user') .then(function (user) { return user.address; }) .then(function (obj) { console.log(obj); // -> 'San Francisco' }); }; 

Je pense que les réponses précédentes sont correctes, mais voici un autre exemple (juste un fyi, succès () et error () sont déconseillés selon AngularJS Page principale :

 $http .get('http://someendpoint/maybe/returns/JSON') .then(function(response) { return response.data; }).catch(function(e) { console.log('Error: ', e); throw e; }).finally(function() { console.log('This finally block'); }); 

Quel type de granularité recherchez-vous? Vous pouvez généralement vous en tirer avec:

 $http.get(url).then( //success function function(results) { //do something w/results.data }, //error function function(err) { //handle error } ); 

J’ai trouvé que “finalement” et “attraper” sont mieux quand on enchaîne plusieurs promesses.

Dans les cas angulars $ http, la fonction success () et error () aura un object réponse non déballé, donc la signature de rappel serait comme $ http (…). Succès (fonction (données, status, en-têtes, config))

pour alors (), vous devrez probablement traiter l’object de réponse brut. tel que publié dans le document d’API AngularJS $ http

 $http({ url: $scope.url, method: $scope.method, cache: $templateCache }) .success(function(data, status) { $scope.status = status; $scope.data = data; }) .error(function(data, status) { $scope.data = data || 'Request failed'; $scope.status = status; }); 

Le dernier .catch (…) n’aura pas besoin, sauf s’il y a une nouvelle erreur dans la chaîne de promesses précédente.

Je le fais comme le suggère Bradley Braithwaite dans son blog :

 app .factory('searchService', ['$q', '$http', function($q, $http) { var service = {}; service.search = function search(query) { // We make use of Angular's $q library to create the deferred instance var deferred = $q.defer(); $http .get('http://localhost/v1?=q' + query) .success(function(data) { // The promise is resolved once the HTTP call is successful. deferred.resolve(data); }) .error(function(reason) { // The promise is rejected if there is an error with the HTTP call. deferred.reject(reason); }); // The promise is returned to the caller return deferred.promise; }; return service; }]) .controller('SearchController', ['$scope', 'searchService', function($scope, searchService) { // The search service returns a promise API searchService .search($scope.query) .then(function(data) { // This is set when the promise is resolved. $scope.results = data; }) .catch(function(reason) { // This is set in the event of an error. $scope.error = 'There has been an error: ' + reason; }); }]) 

Points clés:

  • La fonction de résolution est liée à la fonction .then de notre contrôleur, c’est-à-dire que tout va bien, nous pouvons donc tenir notre promesse et la résoudre.

  • La fonction de rejet est liée à la fonction .catch de notre contrôleur, c’est-à-dire que quelque chose ne va pas, nous ne pouvons donc pas tenir notre promesse et la rejeter.

Il est assez stable et sûr et si vous avez d’autres conditions pour rejeter la promesse, vous pouvez toujours filtrer vos données dans la fonction succès et appeler deferred.reject(anotherReason) avec la raison du rejet.

Comme Ryan Vice l’a suggéré dans les commentaires , cela ne sera peut-être pas utile, à moins que vous ne manipuliez un peu la réponse, pour ainsi dire.

Comme le success et l’ error sont obsolètes depuis la version 1.4, il est préférable d’utiliser les méthodes de promesses régulières et d’ catch et transformer la réponse dans ces méthodes et de renvoyer la promesse de cette réponse transformée.

Je montre le même exemple avec les deux approches et une troisième approche intermédiaire:

approche success et error ( success et error renvoient une promesse de réponse HTTP, nous avons donc besoin de l’aide de $q pour renvoyer une promesse de données):

 function search(query) { // We make use of Angular's $q library to create the deferred instance var deferred = $q.defer(); $http.get('http://localhost/v1?=q' + query) .success(function(data,status) { // The promise is resolved once the HTTP call is successful. deferred.resolve(data); }) .error(function(reason,status) { // The promise is rejected if there is an error with the HTTP call. if(reason.error){ deferred.reject({text:reason.error, status:status}); }else{ //if we don't get any answers the proxy/api will probably be down deferred.reject({text:'whatever', status:500}); } }); // The promise is returned to the caller return deferred.promise; }; 

then et catch approche (c’est un peu plus difficile à tester, à cause du lancer):

 function search(query) { var promise=$http.get('http://localhost/v1?=q' + query) .then(function (response) { // The promise is resolved once the HTTP call is successful. return response.data; },function(reason) { // The promise is rejected if there is an error with the HTTP call. if(reason.statusText){ throw reason; }else{ //if we don't get any answers the proxy/api will probably be down throw {statusText:'Call error', status:500}; } }); return promise; } 

Il y a une solution à mi-chemin (de cette façon, vous pouvez éviter le throw et de toute façon vous devrez probablement utiliser $q pour vous moquer du comportement prometteur dans vos tests):

 function search(query) { // We make use of Angular's $q library to create the deferred instance var deferred = $q.defer(); $http.get('http://localhost/v1?=q' + query) .then(function (response) { // The promise is resolved once the HTTP call is successful. deferred.resolve(response.data); },function(reason) { // The promise is rejected if there is an error with the HTTP call. if(reason.statusText){ deferred.reject(reason); }else{ //if we don't get any answers the proxy/api will probably be down deferred.reject({statusText:'Call error', status:500}); } }); // The promise is returned to the caller return deferred.promise; } 

Tout type de commentaire ou de correction est le bienvenu.