Come chiamare http $ sincrono con AngularJS


132

Esiste un modo per effettuare una chiamata sincrona con AngularJS?

La documentazione di AngularJS non è molto esplicita o estesa per capire alcune cose di base.

SU UN SERVIZIO:

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

Vedi anche groups.google.com/d/topic/angular/qagzXXhS_VI/discussion per alcune idee su come gestire il comportamento asincrono: eventi, $ watch, precaricamento sul lato server, utilizza la promessa restituita da $ http.
Mark Rajcok,

1
L'asincrono è sempre meglio, specialmente quando hai delle promesse.
Andrew Joslin,

Molte volte, è possibile evitare chiamate sincrone. Guarda come funziona $ resource stackoverflow.com/questions/11966252/… .
honzajde,

3
@AndrewJoslin Asincrono è peggio quando è necessaria la consegna ordinata.
Stijn Van Antwerpen,

Risposte:


113

Non al momento. Se guardi il codice sorgente (da questo momento nell'ottobre 2012) , vedrai che la chiamata a XHR aperta è in realtà codificata per essere asincrona (il terzo parametro è vero):

 xhr.open(method, url, true);

Dovresti scrivere il tuo servizio che ha fatto chiamate sincrone. Generalmente non è qualcosa che di solito vorresti fare a causa della natura dell'esecuzione di JavaScript finirai per bloccare tutto il resto.

... ma ... se si desidera bloccare tutto il resto, forse dovresti esaminare le promesse e il servizio $ q . Ti consente di attendere fino a quando non viene eseguita una serie di azioni asincrone e quindi eseguire qualcosa una volta completate. Non so quale sia il tuo caso d'uso, ma potrebbe valere la pena dare un'occhiata.

A parte questo, se hai intenzione di pubblicare le tue, maggiori informazioni su come effettuare chiamate Ajax sincrone e asincrone sono disponibili qui .

Spero sia utile.


12
Potete per favore codificare lo snippet per ottenere usando il servizio $ q. Ho provato molte opzioni ma funziona in modo asincrono.
Venkat,

1
Ci sono luoghi in cui può avere senso, ad es. Proprio quando l'utente chiude il browser (onbeforeunload), se si desidera salvare è necessario inviare una richiesta di sincronizzazione, un'altra opzione è mostrare un annullamento della finestra di dialogo, quindi riavviare la finestra?
Braulio,

2
@Venkat: so che questa è una risposta tardiva, ma come ho detto nella risposta, la chiamata sarà sempre "asincrona", devi solo usare $ q per far aspettare la risposta, quindi continuare la tua logica all'interno del .then(callback). qualcosa come: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh,

3
Il seguente video mi ha aiutato a studiare le promesse AngularJS Promises con $ q
Ilya Palkin,

1
@BenLesh Non sono contrario al tempo che hai inserito o al momento in cui qualcuno ha inserito. Sono libero di votare in negativo la tua risposta e dire che mi sarebbe stato utile se fosse stato fornito un esempio. Ho visto la tua risposta, non mi ha aiutato, quindi ho votato in giù e chiuso la scheda, e sono tornato su Google per cercare di trovare una risposta che mi fosse più utile. Non è la fine del mondo quando qualcuno vota la tua risposta e ti dice come potrebbe essere migliorata. Avresti preferito che ho votato verso il basso senza lasciare un commento sul perché? Solo essere onesto.
circuiteria

12

Ho lavorato con una fabbrica integrata con il completamento automatico di Google Maps e le promesse fatte, spero che tu serva.

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

devi solo sostituire il servizio di completamento automatico con questa richiesta con $ http incuida prima della fabbrica.

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

e $ http richiesta con

 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 domanda stessa deve essere fatta su:

deferred.resolve(varResult); 

quando hai fatto bene e la richiesta:

deferred.reject(error); 

quando si verifica un errore, quindi:

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

Di recente mi sono imbattuto in una situazione in cui volevo effettuare chiamate a $ http attivate da una ricarica della pagina. La soluzione con cui sono andato:

  1. Incapsula le due chiamate in funzioni
  2. Passa la seconda chiamata $ http come richiamata nella seconda funzione
  3. Chiama la seconda funzione in apon .success

E se fosse un ciclo for, con n volte che chiama server.
mithun,

2

Ecco un modo per farlo in modo asincrono e gestire cose come faresti normalmente. Tutto è ancora condiviso. Si ottiene un riferimento all'oggetto che si desidera aggiornare. Ogni volta che lo aggiorni nel tuo servizio, viene aggiornato a livello globale senza dover guardare o restituire una promessa. Questo è davvero bello perché puoi aggiornare l'oggetto sottostante dall'interno del servizio senza dover mai ricollegare. Usando Angular come deve essere usato. Penso che sia probabilmente una cattiva idea rendere sincrono $ http.get / post. Avrai un notevole ritardo nello 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();

E da qualche parte in una vista:

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

0

Poiché la sincronizzazione di XHR è deprecata, è meglio non fare affidamento su di essa. Se è necessario eseguire una richiesta POST di sincronizzazione, è possibile utilizzare i seguenti helper all'interno di un servizio per simulare un post del modulo.

Funziona creando un modulo con input nascosti che viene pubblicato nell'URL specificato.

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

Modifica come richiesto per le tue esigenze.


-4

Che dire di concludere la chiamata in un Promise.all()metodo, ad es

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

Secondo MDN

Promise.all attende tutti gli adempimenti (o il primo rifiuto)


di cosa stai parlando? la domanda non ha nulla a che fare con le molteplici promesse ...
Ovidiu Dolha,

Aspetterà il completamento di una o più promesse!
Manjit Dosanjh,

hai usato questo per vedere come funziona? Promise.all restituirà un'altra promessa, non trasforma l'asincronismo in chiamate di sincronizzazione
Ovidiu Dolha

Hmm ... sembra che la documentazione MDN possa essere ambigua ... In realtà non ASPETTA come indicato nella loro documentazione.
Manjit Dosanjh,

Benvenuti in SO. Si prega di leggere questo how-to-risposta per la fornitura di risposta di qualità.
thewaywewere
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.