$ Http Synchroner Aufruf mit AngularJS


132

Gibt es eine Möglichkeit, einen synchronen Anruf mit AngularJS zu tätigen?

Die AngularJS-Dokumentation ist nicht sehr explizit oder umfangreich, um einige grundlegende Dinge herauszufinden.

AUF EINEM DIENST:

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

Unter groups.google.com/d/topic/angular/qagzXXhS_VI/discussion finden Sie einige Ideen zum Umgang mit dem asynchronen Verhalten: Ereignisse, $ watch, Preload auf der Serverseite, Verwenden Sie das von $ http zurückgegebene Versprechen.
Mark Rajcok

1
Asynchron ist immer besser, besonders wenn Sie Versprechen haben.
Andrew Joslin

Oft können Sie synchrone Anrufe vermeiden. Sehen Sie, wie $ resource funktioniert stackoverflow.com/questions/11966252/… .
Honzajde

3
@ AndrewJoslin Asynchron ist schlechter, wenn Sie eine bestellte Lieferung benötigen.
Stijn Van Antwerpen

Antworten:


113

Derzeit nicht. Wenn Sie sich den Quellcode ansehen (ab diesem Zeitpunkt, Oktober 2012) , werden Sie feststellen, dass der Aufruf von XHR open tatsächlich fest codiert ist, um asynchron zu sein (der dritte Parameter ist wahr):

 xhr.open(method, url, true);

Sie müssten Ihren eigenen Dienst schreiben, der synchrone Anrufe tätigt. Im Allgemeinen möchten Sie dies aufgrund der Art der JavaScript-Ausführung normalerweise nicht tun, um alles andere zu blockieren.

... aber ... wenn das Blockieren von allem anderen tatsächlich gewünscht ist, sollten Sie sich vielleicht die Versprechen und den $ q-Service ansehen . Sie können warten, bis eine Reihe von asynchronen Aktionen ausgeführt wurden, und dann etwas ausführen, sobald alle abgeschlossen sind. Ich weiß nicht, was Ihr Anwendungsfall ist, aber das könnte einen Blick wert sein.

Abgesehen davon finden Sie hier weitere Informationen dazu, wie Sie synchrone und asynchrone Ajax-Aufrufe tätigen können .

Ich hoffe das ist hilfreich.


12
Können Sie bitte ein Snippet codieren, um es mit dem Dienst $ q zu erreichen? Ich habe viele Optionen ausprobiert, aber es funktioniert asynchron.
Venkat

1
Es gibt Stellen, an denen dies sinnvoll sein kann, z. B. wenn der Benutzer den Browser schließt (vor dem Herunterladen). Wenn Sie speichern möchten, müssen Sie eine Synchronisierungsanforderung senden. Eine andere Option besteht darin, einen Dialogabbruch anzuzeigen und das Fenster dann erneut zu schließen.
Braulio

2
@Venkat: Ich weiß, dass dies eine späte Antwort ist, aber wie ich in der Antwort sagte, wird der Anruf immer "asynchron" sein. Sie müssen nur $ q verwenden, um auf die Antwort zu warten , und dann Ihre Logik innerhalb der fortsetzen .then(callback). so etwas wie : doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh

3
Das folgende Video hat mir geholfen, Versprechen zu studieren. AngularJS Versprechen mit $ q
Ilya Palkin

1
@BenLesh Ich bin nicht unzufrieden mit der Zeit, die Sie eingegeben haben, oder der Zeit, die jemand eingegeben hat. Es steht mir frei, Ihre Antwort herunterzustimmen und zu sagen, dass es für mich hilfreich gewesen wäre, wenn ein Beispiel angegeben worden wäre. Ich habe Ihre Antwort gesehen, sie hat mir nicht geholfen, also habe ich sie abgelehnt, den Tab geschlossen und bin zu Google zurückgekehrt, um eine Antwort zu finden, die für mich hilfreicher war. Es ist nicht das Ende der Welt, wenn jemand Ihre Antwort ablehnt und Ihnen sagt, wie sie verbessert werden könnte. Hätten Sie es vorgezogen, wenn ich abgelehnt hätte, ohne einen Kommentar zu hinterlassen, warum? Um ehrlich zu sein.
Schaltung

12

Ich habe mit einer Fabrik gearbeitet, die in die automatische Vervollständigung von Google Maps integriert ist, und Versprechungen gemacht. Ich hoffe, Sie dienen.

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

Sie müssen den autocompleteService nur durch diese Anforderung ersetzen, indem $ http incuida vor der Fabrik steht.

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

und $ http Anfrage mit

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

});

Die Frage selbst ist zu stellen:

deferred.resolve(varResult); 

wenn du es gut gemacht hast und die Anfrage:

deferred.reject(error); 

wenn es einen Fehler gibt und dann:

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

Ich bin kürzlich in eine Situation geraten, in der ich $ http-Anrufe tätigen wollte, die durch ein erneutes Laden der Seite ausgelöst wurden. Die Lösung, mit der ich mich entschieden habe:

  1. Kapseln Sie die beiden Aufrufe in Funktionen
  2. Übergeben Sie den zweiten $ http-Aufruf als Rückruf an die zweite Funktion
  3. Rufen Sie die zweite Funktion in apon .success auf

Was ist, wenn es eine for-Schleife ist, bei der der Server n-mal aufgerufen wird?
Mithun

2

Hier ist eine Möglichkeit, wie Sie dies asynchron tun und Dinge wie gewohnt verwalten können. Alles wird noch geteilt. Sie erhalten einen Verweis auf das Objekt, das Sie aktualisieren möchten. Wann immer Sie dies in Ihrem Dienst aktualisieren, wird es global aktualisiert, ohne dass Sie ein Versprechen beobachten oder zurückgeben müssen. Dies ist sehr hilfreich, da Sie das zugrunde liegende Objekt innerhalb des Dienstes aktualisieren können, ohne es erneut binden zu müssen. Verwenden Sie Angular so, wie es verwendet werden soll. Ich denke, es ist wahrscheinlich eine schlechte Idee, $ http.get / post synchron zu machen. Sie erhalten eine merkliche Verzögerung im Skript.

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

Und irgendwo in einer Ansicht:

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

0

Da die Synchronisierung von XHR veraltet ist, sollten Sie sich nicht darauf verlassen. Wenn Sie eine POST-Synchronisierungsanforderung ausführen müssen, können Sie die folgenden Helfer innerhalb eines Dienstes verwenden, um einen Formularbeitrag zu simulieren.

Dazu wird ein Formular mit ausgeblendeten Eingaben erstellt, das an die angegebene URL gesendet wird.

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

Ändern Sie sie nach Bedarf.


-4

Was ist mit dem Umschließen Ihres Aufrufs in eine Promise.all()Methode, dh

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

Laut MDN

Promise.all wartet auf alle Erfüllungen (oder die erste Ablehnung)


Worüber redest du? Die Frage hat nichts mit mehreren Versprechungen zu tun ...
Ovidiu Dolha

Es wird warten, bis ein oder mehrere Versprechen erfüllt sind!
Manjit Dosanjh

Haben Sie dies verwendet, um zu sehen, wie es funktioniert? Promise.all wird ein weiteres Versprechen zurückgeben, es verwandelt Async nicht in Sync-Aufrufe
Ovidiu Dolha

Hmm ... es scheint, dass die MDN-Dokumentation nicht eindeutig ist ... Sie wartet nicht wie in der Dokumentation angegeben.
Manjit Dosanjh

Willkommen bei SO. Bitte lesen Sie diese Anleitung, um eine qualitativ hochwertige Antwort zu erhalten.
thewaywewere
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.