Comment appeler $ http synchrone avec AngularJS


132

Existe-t-il un moyen de passer un appel synchrone avec AngularJS?

La documentation AngularJS n'est pas très explicite ou exhaustive pour comprendre certains éléments de base.

SUR UN SERVICE:

myService.getByID = function (id) {
    var retval = null;

    $http({
        url: "/CO/api/products/" + id,
        method: "GET"
    }).success(function (data, status, headers, config) {

        retval = data.Data;

    });

    return retval;
}

Voir aussi groups.google.com/d/topic/angular/qagzXXhS_VI/discussion pour quelques idées sur la façon de gérer le comportement asynchrone: événements, $ watch, préchargement côté serveur, utilisez la promesse renvoyée par $ http.
Mark Rajcok

1
L'asynchrone est toujours mieux, surtout lorsque vous avez des promesses.
Andrew Joslin

Plusieurs fois, vous pouvez éviter les appels synchrones. Découvrez comment fonctionne $ resource stackoverflow.com/questions/11966252/… .
honzajde

3
@AndrewJoslin Asynchronous est pire lorsque vous avez besoin d'une livraison commandée.
Stijn Van Antwerpen le

Réponses:


113

Pas actuellement. Si vous regardez le code source (à partir de ce moment d'octobre 2012) , vous verrez que l'appel à XHR open est en fait codé en dur pour être asynchrone (le troisième paramètre est vrai):

 xhr.open(method, url, true);

Vous auriez besoin d'écrire votre propre service qui effectue des appels synchrones. En général, ce n'est pas quelque chose que vous voudrez généralement faire en raison de la nature de l'exécution de JavaScript, vous finirez par bloquer tout le reste.

... mais .. si vous souhaitez bloquer tout le reste, vous devriez peut-être vous pencher sur les promesses et le service $ q . Il vous permet d'attendre qu'un ensemble d'actions asynchrones soit effectué, puis d'exécuter quelque chose une fois qu'elles sont toutes terminées. Je ne sais pas quel est votre cas d'utilisation, mais cela vaut peut-être la peine d'être examiné.

En dehors de cela, si vous allez rouler votre propre, plus d' informations sur la façon de faire des appels ajax synchrones et asynchrones peuvent être trouvés ici .

J'espère que c'est utile.


12
Pouvez-vous s'il vous plaît un extrait de code à réaliser en utilisant le service $ q. J'ai essayé de nombreuses options mais cela fonctionne de manière asynchrone.
Venkat

1
Il y a des endroits où cela peut avoir du sens, par exemple juste lorsque l'utilisateur ferme le navigateur (avant le déchargement), si vous voulez enregistrer, vous devez envoyer une demande de synchronisation, une autre option est d'afficher une boîte de dialogue d'annulation, puis de relancer la fenêtre fermer?
Braulio

2
@Venkat: Je sais que c'est une réponse tardive, mais comme je l'ai dit dans la réponse, l'appel sera toujours "asynchrone", il vous suffit d'utiliser $ q pour le faire attendre la réponse, puis continuez votre logique à l'intérieur du .then(callback). quelque chose comme: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh

3
La vidéo suivante m'a aidé à étudier les promesses AngularJS Promises avec $ q
Ilya Palkin

1
@BenLesh Je ne suis pas indifférent au temps que vous y consacrez, ni au temps que quelqu'un y consacre. Je suis libre de voter contre votre réponse et de dire que cela m'aurait été utile si un exemple avait été fourni. J'ai vu votre réponse, cela ne m'a pas aidé, alors j'ai voté contre et fermé l'onglet, et je suis retourné sur Google pour essayer de trouver une réponse qui m'était plus utile. Ce n'est pas la fin du monde quand quelqu'un vote à la baisse votre réponse et vous dit comment elle pourrait être améliorée. Auriez-vous préféré que je vote à la baisse sans laisser de commentaire sur la raison? Juste être honnête.
circuiterie

12

J'ai travaillé avec une usine intégrée avec la saisie semi-automatique de google maps et les promesses faites, j'espère que vous servez.

http://jsfiddle.net/the_pianist2/vL9nkfe3/1/

il vous suffit de remplacer autocompleteService par cette requête avec $ http incuida devant l'usine.

app.factory('Autocomplete', function($q, $http) {

et $ http request avec

 var deferred = $q.defer();
 $http.get('urlExample').
success(function(data, status, headers, config) {
     deferred.resolve(data);
}).
error(function(data, status, headers, config) {
     deferred.reject(status);
});
 return deferred.promise;

<div ng-app="myApp">
  <div ng-controller="myController">
  <input type="text" ng-model="search"></input>
  <div class="bs-example">
     <table class="table" >
        <thead>
           <tr>
              <th>#</th>
              <th>Description</th>
           </tr>
        </thead>
        <tbody>
           <tr ng-repeat="direction in directions">
              <td>{{$index}}</td>
              <td>{{direction.description}}</td>
           </tr>
        </tbody>
     </table>
  </div>

'use strict';
 var app = angular.module('myApp', []);

  app.factory('Autocomplete', function($q) {
    var get = function(search) {
    var deferred = $q.defer();
    var autocompleteService = new google.maps.places.AutocompleteService();
    autocompleteService.getPlacePredictions({
        input: search,
        types: ['geocode'],
        componentRestrictions: {
            country: 'ES'
        }
    }, function(predictions, status) {
        if (status == google.maps.places.PlacesServiceStatus.OK) {
            deferred.resolve(predictions);
        } else {
            deferred.reject(status);
        }
    });
    return deferred.promise;
};

return {
    get: get
};
});

app.controller('myController', function($scope, Autocomplete) {
$scope.$watch('search', function(newValue, oldValue) {
    var promesa = Autocomplete.get(newValue);
    promesa.then(function(value) {
        $scope.directions = value;
    }, function(reason) {
        $scope.error = reason;
    });
 });

});

la question elle-même est à poser sur:

deferred.resolve(varResult); 

quand vous avez bien fait et la demande:

deferred.reject(error); 

quand il y a une erreur, et ensuite:

return deferred.promise;

5
var EmployeeController = ["$scope", "EmployeeService",
        function ($scope, EmployeeService) {
            $scope.Employee = {};
            $scope.Save = function (Employee) {                
                if ($scope.EmployeeForm.$valid) {
                    EmployeeService
                        .Save(Employee)
                        .then(function (response) {
                            if (response.HasError) {
                                $scope.HasError = response.HasError;
                                $scope.ErrorMessage = response.ResponseMessage;
                            } else {

                            }
                        })
                        .catch(function (response) {

                        });
                }
            }
        }]


var EmployeeService = ["$http", "$q",
            function ($http, $q) {
                var self = this;

                self.Save = function (employee) {
                    var deferred = $q.defer();                
                    $http
                        .post("/api/EmployeeApi/Create", angular.toJson(employee))
                        .success(function (response, status, headers, config) {
                            deferred.resolve(response, status, headers, config);
                        })
                        .error(function (response, status, headers, config) {
                            deferred.reject(response, status, headers, config);
                        });

                    return deferred.promise;
                };

4

J'ai récemment rencontré une situation dans laquelle je voulais faire des appels à $ http déclenchés par un rechargement de page. La solution avec laquelle je suis allé:

  1. Encapsulez les deux appels dans des fonctions
  2. Passez le deuxième appel $ http comme rappel dans la deuxième fonction
  3. Appelez la deuxième fonction dans apon .success

Et si c'était une boucle for, avec n fois appel au serveur.
mithun

2

Voici un moyen de le faire de manière asynchrone et de gérer les choses comme vous le feriez normalement. Tout est toujours partagé. Vous obtenez une référence à l'objet que vous souhaitez mettre à jour. Chaque fois que vous mettez à jour cela dans votre service, il est mis à jour globalement sans avoir à regarder ou à retourner une promesse. C'est vraiment bien car vous pouvez mettre à jour l'objet sous-jacent à partir du service sans jamais avoir à relier. Utiliser Angular comme il est censé être utilisé. Je pense que c'est probablement une mauvaise idée de rendre $ http.get / post synchrone. Vous obtiendrez un retard notable dans le script.

app.factory('AssessmentSettingsService', ['$http', function($http) {
    //assessment is what I want to keep updating
    var settings = { assessment: null };

    return {
        getSettings: function () {
             //return settings so I can keep updating assessment and the
             //reference to settings will stay in tact
             return settings;
        },
        updateAssessment: function () {
            $http.get('/assessment/api/get/' + scan.assessmentId).success(function(response) {
                //I don't have to return a thing.  I just set the object.
                settings.assessment = response;
            });
        }
    };
}]);

    ...
        controller: ['$scope', '$http', 'AssessmentSettingsService', function ($scope, as) {
            $scope.settings = as.getSettings();
            //Look.  I can even update after I've already grabbed the object
            as.updateAssessment();

Et quelque part dans une vue:

<h1>{{settings.assessment.title}}</h1>

0

Étant donné que la synchronisation XHR est obsolète, il vaut mieux ne pas s'y fier. Si vous devez effectuer une requête POST de synchronisation, vous pouvez utiliser les assistants suivants à l'intérieur d'un service pour simuler une publication de formulaire.

Cela fonctionne en créant un formulaire avec des entrées masquées qui est publié à l'URL spécifiée.

//Helper to create a hidden input
function createInput(name, value) {
  return angular
    .element('<input/>')
    .attr('type', 'hidden')
    .attr('name', name)
    .val(value);
}

//Post data
function post(url, data, params) {

    //Ensure data and params are an object
    data = data || {};
    params = params || {};

    //Serialize params
    const serialized = $httpParamSerializer(params);
    const query = serialized ? `?${serialized}` : '';

    //Create form
    const $form = angular
        .element('<form/>')
        .attr('action', `${url}${query}`)
        .attr('enctype', 'application/x-www-form-urlencoded')
        .attr('method', 'post');

    //Create hidden input data
    for (const key in data) {
        if (data.hasOwnProperty(key)) {
            const value = data[key];
            if (Array.isArray(value)) {
                for (const val of value) {
                    const $input = createInput(`${key}[]`, val);
                    $form.append($input);
                }
            }
            else {
                const $input = createInput(key, value);
                $form.append($input);
            }
        }
    }

    //Append form to body and submit
    angular.element(document).find('body').append($form);
    $form[0].submit();
    $form.remove();
}

Modifiez selon vos besoins.


-4

Qu'en est-il de l'encapsulation de votre appel dans une Promise.all()méthode ie

Promise.all([$http.get(url).then(function(result){....}, function(error){....}])

Selon MDN

Promise.all attend tous les accomplissements (ou le premier rejet)


Qu'est-ce que tu racontes? la question n'a rien à voir avec de multiples promesses ...
Ovidiu Dolha

Il attendra qu'une ou plusieurs promesses se terminent!
Manjit Dosanjh

avez-vous utilisé ceci pour voir comment cela fonctionne? Promise.all renverra une autre promesse, il ne transforme pas l'async en appels synchronisés
Ovidiu Dolha

Hmm ... il semble que la documentation MDN puisse être ambiguë ... Elle n'ATTENTE pas réellement comme indiqué dans leur documentation.
Manjit Dosanjh

Bienvenue à SO. Veuillez lire cette réponse pratique pour fournir une réponse de qualité.
thewaywewere
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.