Jak wykonać połączenie synchroniczne $ http z AngularJS


132

Czy istnieje sposób na wykonanie połączenia synchronicznego za pomocą AngularJS?

Dokumentacja AngularJS nie jest zbyt wyraźna ani obszerna, jeśli chodzi o zrozumienie niektórych podstawowych rzeczy.

NA USŁUGI:

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;
}

Zobacz także groups.google.com/d/topic/angular/qagzXXhS_VI/discussion, aby uzyskać kilka pomysłów na temat radzenia sobie z zachowaniem asynchronicznym: zdarzenia, $ watch, wstępne ładowanie po stronie serwera, wykorzystanie obietnicy zwróconej z $ http.
Mark Rajcok

1
Asynchroniczność jest zawsze lepsza, zwłaszcza gdy masz obietnice.
Andrew Joslin,

Często można uniknąć połączeń synchronicznych. Zobacz, jak działa $ resource stackoverflow.com/questions/11966252/… .
honzajde,

4
@AndrewJoslin Asynchroniczny jest gorszy, gdy potrzebujesz zamówionej dostawy.
Stijn Van Antwerpen

Odpowiedzi:


113

Obecnie nie. Jeśli spojrzysz na kod źródłowy (od tego momentu w październiku 2012) , zobaczysz, że wywołanie XHR open jest w rzeczywistości zakodowane na stałe jako asynchroniczne (trzeci parametr to prawda):

 xhr.open(method, url, true);

Musiałbyś napisać własną usługę, która wykonywała połączenia synchroniczne. Zwykle nie jest to coś, co zwykle chcesz zrobić, ponieważ charakter wykonywania JavaScript spowoduje zablokowanie wszystkiego innego.

... ale .. jeśli blokowanie wszystkiego innego jest rzeczywiście pożądane, może powinieneś spojrzeć na obietnice i usługę $ q . Pozwala poczekać, aż zestaw asynchronicznych działań zostanie wykonany, a następnie wykonać coś, gdy wszystkie zostaną ukończone. Nie wiem, jaki jest twój przypadek użycia, ale może warto się temu przyjrzeć.

Poza tym, jeśli masz zamiar tworzyć własne, więcej informacji o tym, jak wykonywać synchroniczne i asynchroniczne wywołania Ajax, można znaleźć tutaj .

Mam nadzieję, że to jest pomocne.


12
Czy możesz prosić fragment kodu, aby to osiągnąć za pomocą usługi $ q. Wypróbowałem wiele opcji, ale działa w sposób asynchroniczny.
Venkat

1
Są miejsca, w których może to mieć sens, np. Tylko wtedy, gdy użytkownik zamyka przeglądarkę (onb przed załadowaniem), jeśli chcesz zapisać, musisz wysłać żądanie synchronizacji, inną opcją jest wyświetlenie okna dialogowego anuluj, a następnie ponowne uruchomienie okna zamknąć?
Braulio

2
@Venkat: Wiem, że to spóźniona odpowiedź, ale jak powiedziałem w odpowiedzi, połączenie zawsze będzie „asynchroniczne”, wystarczy użyć $ q, aby poczekać na odpowiedź, a następnie kontynuować logikę wewnątrz .then(callback). coś takiego: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh

3
Poniższy film pomógł mi w studiowaniu obietnic AngularJS Promises z $ q
Ilya Palkin

1
@BenLesh Nie jestem niedoceniany, jeśli chodzi o czas, który poświęciłeś, ani czas, który ktoś poświęca. Mogę głosować w dół za twoją odpowiedź i powiedzieć, że byłoby to dla mnie pomocne, gdyby podano przykład. Widziałem twoją odpowiedź, nie pomogło mi, więc przegłosowałem ją, zamknąłem kartę i wróciłem do Google, aby spróbować znaleźć odpowiedź, która byłaby dla mnie bardziej pomocna. To nie koniec świata, gdy ktoś negatywnie ocenia Twoją odpowiedź i mówi, jak można ją poprawić. Czy wolałbyś, żebym zagłosował w dół bez komentowania, dlaczego? Po prostu jestem szczery.
obwody elektryczne

12

Pracowałem z fabryką zintegrowaną z autouzupełnianiem map Google i złożonymi obietnicami, mam nadzieję, że będziesz służyć.

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

wystarczy zamienić usługę autocompleteService na to żądanie z wartością $ http incuida znajdującą się przed fabryką.

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

i żądanie $ http z

 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;
    });
 });

});

samo pytanie dotyczy:

deferred.resolve(varResult); 

kiedy dobrze się spisałeś i prośba:

deferred.reject(error); 

gdy wystąpi błąd, a następnie:

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

Niedawno spotkałem się z sytuacją, w której chciałem wykonywać połączenia do $ http wywołane przeładowaniem strony. Rozwiązanie, które wybrałem:

  1. Hermetyzuj te dwa wywołania w funkcje
  2. Przekaż drugie wywołanie $ http jako wywołanie zwrotne do drugiej funkcji
  3. Wywołaj drugą funkcję w apon .success

A co, jeśli jest to pętla for z n razy wywołującą serwer.
mithun

2

Oto sposób, w jaki możesz to zrobić asynchronicznie i zarządzać rzeczami tak, jak zwykle. Wszystko jest nadal udostępniane. Otrzymasz odniesienie do obiektu, który chcesz zaktualizować. Za każdym razem, gdy aktualizujesz to w swojej usłudze, jest aktualizowane globalnie bez konieczności oglądania lub zwracania obietnicy. Jest to naprawdę fajne, ponieważ możesz zaktualizować podstawowy obiekt z poziomu usługi bez konieczności ponownego wiązania. Używanie Angulara zgodnie z przeznaczeniem. Myślę, że synchronizacja $ http.get / post to prawdopodobnie zły pomysł. Otrzymasz zauważalne opóźnienie w skrypcie.

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();

I gdzieś w widoku:

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

0

Ponieważ synchronizacja XHR jest przestarzała, najlepiej na tym nie polegać. Jeśli musisz wykonać żądanie POST synchronizacji, możesz użyć następujących pomocników w usłudze, aby zasymulować post formularza.

Działa poprzez utworzenie formularza z ukrytymi danymi wejściowymi, który jest wysyłany pod określony adres URL.

//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();
}

Zmodyfikuj zgodnie z wymaganiami.


-4

A co z opakowaniem wywołania w Promise.all()metodę np

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

Według MDN

Obietnica. Wszystko czeka na wszystkie wypełnienia (lub pierwsze odrzucenie)


o czym mówisz? pytanie nie ma nic wspólnego z wieloma obietnicami ...
Ovidiu Dolha

Będzie czekać na wypełnienie jednej lub więcej obietnic!
Manjit Dosanjh

czy użyłeś tego, aby zobaczyć, jak to działa? Promise.all zwróci kolejną obietnicę, nie przekształci asynchronizacji w połączenia synchronizacyjne
Ovidiu Dolha

Hmm ... wygląda na to, że dokumentacja MDN może być niejednoznaczna ... W rzeczywistości nie CZEKA, jak podano w ich dokumentacji.
Manjit Dosanjh

Witamy w SO. Przeczytaj ten poradnik odpowiedzi, aby udzielić dobrej odpowiedzi.
2017
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.