Cara $ http Panggilan sinkron dengan AngularJS


132

Apakah ada cara untuk melakukan panggilan sinkron dengan AngularJS?

Dokumentasi AngularJS tidak terlalu eksplisit atau luas untuk mencari tahu beberapa hal dasar.

PADA LAYANAN:

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

Lihat juga groups.google.com/d/topic/angular/qagzXXhS_VI/discussion untuk beberapa gagasan tentang cara menangani perilaku asinkron: peristiwa, $ tonton, preload di sisi server, gunakan janji yang dikembalikan dari $ http.
Mark Rajcok

1
Asynchronous selalu lebih baik, terutama ketika Anda memiliki janji.
Andrew Joslin

Sering kali, Anda dapat menghindari panggilan sinkron. Lihat cara kerja $ resource stackoverflow.com/questions/11966252/… .
honzajde

3
@AndrewJoslin Asynchronous lebih buruk ketika Anda perlu pengiriman pesanan.
Stijn Van Antwerpen

Jawaban:


113

Tidak saat ini. Jika Anda melihat kode sumber (mulai saat ini Oktober 2012) , Anda akan melihat bahwa panggilan ke XHR open sebenarnya dikodekan keras menjadi asinkron (parameter ketiga benar):

 xhr.open(method, url, true);

Anda harus menulis layanan Anda sendiri yang melakukan panggilan sinkron. Biasanya itu bukan sesuatu yang biasanya ingin Anda lakukan karena sifat eksekusi JavaScript yang pada akhirnya akan memblokir semua yang lain.

... tapi .. jika memblokir semua hal lain sebenarnya diinginkan, mungkin Anda harus melihat ke dalam janji dan layanan $ q . Ini memungkinkan Anda untuk menunggu hingga serangkaian tindakan asinkron dilakukan, dan kemudian menjalankan sesuatu setelah semuanya selesai. Saya tidak tahu apa itu use case Anda, tapi itu mungkin layak untuk dilihat.

Di luar itu, jika Anda akan memutar sendiri, informasi lebih lanjut tentang cara membuat panggilan ajax sinkron dan asinkron dapat ditemukan di sini .

Saya harap itu membantu.


12
Bisakah Anda memberi kode cuplikan untuk mencapai menggunakan layanan $ q. Saya mencoba banyak opsi tetapi bekerja dengan cara yang tidak sinkron.
Venkat

1
Ada tempat-tempat di mana itu masuk akal, misalnya tepat ketika pengguna menutup browser (onbeforeunload), jika Anda ingin menyimpan Anda harus mengirim permintaan sinkronisasi, opsi lain adalah menampilkan dialog batal, dan kemudian luncurkan kembali jendela tutup?
Braulio

2
@ Venkat: Saya tahu ini adalah jawaban yang terlambat, tetapi seperti yang saya katakan di jawaban, panggilan akan selalu "asinkron", Anda hanya perlu menggunakan $ q untuk membuatnya menunggu jawaban, kemudian lanjutkan logika Anda di dalam .then(callback). sesuatu seperti: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh

3
Video berikut membantu saya mempelajari janji-janji AngularJS Promises dengan $ q
Ilya Palkin

1
@BenLesh Saya tidak tidak menghargai waktu yang Anda masukkan, atau waktu orang memasukkan. Saya bebas untuk memilih-turunkan jawaban Anda dan mengatakan bahwa akan sangat membantu bagi saya jika contoh diberikan. Saya melihat jawaban Anda, itu tidak membantu saya, jadi saya memilih dan menutup tab, dan kembali ke google untuk mencoba menemukan jawaban yang lebih membantu bagi saya. Ini bukan akhir dunia ketika seseorang memberikan suara untuk jawaban Anda, dan memberi tahu Anda bagaimana hal itu dapat ditingkatkan. Apakah Anda lebih suka saya memilih-bawah tanpa memberikan komentar mengapa? Jujur saja.
sirkuit

12

Saya telah bekerja dengan pabrik yang terintegrasi dengan google maps autocomplete dan janji dibuat, saya harap Anda melayani.

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

Anda hanya perlu mengganti autocompleteService dengan permintaan ini dengan $ http incuida sebelum pabrik.

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

dan permintaan $ http dengan

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

});

pertanyaannya adalah:

deferred.resolve(varResult); 

ketika Anda melakukannya dengan baik dan permintaan:

deferred.reject(error); 

ketika ada kesalahan, dan kemudian:

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

Saya baru-baru ini mengalami situasi di mana saya ingin membuat panggilan $ http dipicu oleh reload halaman. Solusi yang saya gunakan:

  1. Meringkas dua panggilan menjadi fungsi
  2. Lewati panggilan $ http kedua sebagai panggilan balik ke fungsi kedua
  3. Panggil fungsi kedua di apon .success

Bagaimana jika itu untuk loop, dengan n kali memanggil server.
mithun

2

Inilah cara Anda dapat melakukannya secara tidak sinkron dan mengelola hal-hal seperti biasa. Semuanya masih dibagikan. Anda mendapatkan referensi ke objek yang ingin Anda perbarui. Setiap kali Anda memperbarui itu di layanan Anda, itu diperbarui secara global tanpa harus menonton atau mengembalikan janji. Ini sangat bagus karena Anda dapat memperbarui objek yang mendasarinya dari dalam layanan tanpa harus mengulang kembali. Menggunakan Angular sebagaimana seharusnya digunakan. Saya pikir itu mungkin ide yang buruk untuk membuat $ http.get / post yang sinkron. Anda akan mendapatkan keterlambatan nyata dalam skrip.

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

Dan di suatu tempat di tampilan:

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

0

Karena sinkronisasi XHR sedang tidak digunakan, sebaiknya jangan mengandalkan itu. Jika Anda perlu melakukan permintaan POST sinkronisasi, Anda dapat menggunakan pembantu berikut di dalam layanan untuk mensimulasikan posting formulir.

Ini berfungsi dengan membuat formulir dengan input tersembunyi yang diposting ke URL yang ditentukan.

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

Ubah sesuai kebutuhan untuk kebutuhan Anda.


-4

Bagaimana dengan membungkus panggilan Anda dengan Promise.all()metode yaitu

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

Menurut MDN

Promise.all menunggu semua pemenuhan (atau penolakan pertama)


apa yang kamu bicarakan? pertanyaannya tidak ada hubungannya dengan banyak janji ...
Ovidiu Dolha

Itu akan menunggu satu atau lebih janji untuk selesai!
Manjit Dosanjh

Sudahkah Anda menggunakan ini untuk melihat cara kerjanya? Promise.all akan mengembalikan janji lain, itu tidak mengubah async menjadi panggilan sinkronisasi
Ovidiu Dolha

Hmm ... sepertinya dokumentasi MDN mungkin ambigu ... Tidak benar-benar TUNGGU seperti yang dinyatakan dalam dokumentasi mereka.
Manjit Dosanjh

Selamat datang di SO. Baca cara-jawaban ini untuk memberikan jawaban berkualitas.
thewaywewere
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.