วิธีการ $ http การโทรแบบซิงโครนัสกับ AngularJS


132

มีวิธีใดในการโทรแบบซิงโครนัสกับ AngularJS หรือไม่?

เอกสาร AngularJS ไม่ชัดเจนหรือครอบคลุมมากสำหรับการหาข้อมูลพื้นฐานบางอย่าง

ในการบริการ:

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

ดูgroups.google.com/d/topic/angular/qagzXXhS_VI/discussionสำหรับแนวคิดบางประการเกี่ยวกับวิธีจัดการกับพฤติกรรมแบบอะซิงโครนัส: เหตุการณ์, $ watch, โหลดล่วงหน้าทางฝั่งเซิร์ฟเวอร์, ใช้คำสัญญาที่ส่งคืนจาก $ http
Mark Rajcok

1
อะซิงโครนัสดีกว่าเสมอโดยเฉพาะเมื่อคุณมีสัญญา
Andrew Joslin

หลายครั้งคุณสามารถหลีกเลี่ยงการโทรแบบซิงโครนัสได้ ดูวิธีการทำงาน $ ทรัพยากรstackoverflow.com/questions/11966252/...
honzajde

4
@AndrewJoslin Asynchronous แย่กว่าเมื่อคุณต้องสั่งการจัดส่ง
Stijn Van Antwerpen

คำตอบ:


113

ไม่อยู่ในขณะนี้ หากคุณดูซอร์สโค้ด (จากจุดนี้ในเดือน ต.ค. 2555)คุณจะเห็นว่าการเรียก XHR open นั้นฮาร์ดโค้ดเป็นแบบอะซิงโครนัส (พารามิเตอร์ที่สามเป็นจริง):

 xhr.open(method, url, true);

คุณต้องเขียนบริการของคุณเองที่โทรแบบซิงโครนัส โดยทั่วไปแล้วนั่นไม่ใช่สิ่งที่คุณต้องการทำเนื่องจากลักษณะของการเรียกใช้ JavaScript คุณจะปิดกั้นทุกอย่าง

... แต่ .. ถ้าปิดกั้นทุกอย่างอื่นที่เป็นที่ต้องการจริงบางทีคุณควรมีลักษณะเป็นสัญญาและบริการ $ Q ช่วยให้คุณสามารถรอจนกว่าชุดของการกระทำแบบอะซิงโครนัสจะเสร็จสิ้นจากนั้นจึงดำเนินการบางอย่างเมื่อเสร็จสิ้นทั้งหมด ฉันไม่รู้ว่ากรณีการใช้งานของคุณคืออะไร แต่อาจคุ้มค่าที่จะดู

นอกนั้นถ้าคุณกำลังจะม้วนตัวเองข้อมูลของคุณเพิ่มเติมเกี่ยวกับวิธีที่จะทำให้การซิงโครและไม่ตรงกันโทร Ajax สามารถพบได้ที่นี่

ฉันหวังว่าจะเป็นประโยชน์


12
คุณสามารถกรุณาตัวอย่างโค้ดเพื่อให้บรรลุโดยใช้บริการ $ q ฉันลองใช้ตัวเลือกมากมาย แต่ใช้งานได้ในลักษณะอะซิงโครนัส
Venkat

1
มีสถานที่ที่เหมาะสมเช่นเมื่อผู้ใช้ปิดเบราว์เซอร์ (onbeforeunload) หากคุณต้องการบันทึกคุณต้องส่งคำขอการซิงค์อีกทางเลือกหนึ่งคือแสดงการยกเลิกกล่องโต้ตอบจากนั้นเปิดหน้าต่างใหม่อีกครั้งปิด?
Braulio

2
@Venkat: ฉันรู้ว่านี่เป็นการตอบกลับช้า แต่อย่างที่ฉันพูดในคำตอบการโทรจะเป็น "อะซิงโครนัส" เสมอคุณเพียงแค่ใช้ $ q เพื่อรอการตอบกลับจากนั้นดำเนินการต่อตรรกะของคุณภายใน.then(callback). สิ่งที่ชอบ: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Ben Lesh

3
วิดีโอต่อไปนี้ช่วยฉันในการศึกษาคำสัญญาAngularJS Promises ด้วย $ q
Ilya Palkin

1
@BenLesh ฉันไม่ได้ชื่นชมเวลาที่คุณใส่หรือเวลาที่ใคร ๆ ใส่ฉันมีอิสระที่จะลงคะแนนคำตอบของคุณและบอกว่ามันจะเป็นประโยชน์กับฉันหากมีตัวอย่างให้ ฉันเห็นคำตอบของคุณมันไม่ได้ช่วยฉันฉันจึงลงคะแนนและปิดแท็บนั้นและกลับไปที่ Google เพื่อพยายามหาคำตอบที่เป็นประโยชน์กับฉันมากขึ้น ไม่ใช่จุดจบของโลกเมื่อมีคนโหวตคำตอบของคุณและบอกคุณว่าจะปรับปรุงได้อย่างไร คุณต้องการให้ฉันโหวตโดยไม่แสดงความคิดเห็นว่าทำไม? เพียงแค่ซื่อสัตย์
วงจร

12

ฉันได้ทำงานกับโรงงานที่รวมกับการเติมข้อความอัตโนมัติของ Google Maps และสัญญาไว้ฉันหวังว่าคุณจะให้บริการ

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

คุณจะต้องแทนที่บริการเติมข้อความอัตโนมัติตามคำขอนี้โดยให้ $ http incuida อยู่ก่อนโรงงาน

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

และขอ $ http กับ

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

});

คำถามนั้นจะต้องทำใน:

deferred.resolve(varResult); 

เมื่อคุณทำได้ดีและคำขอ:

deferred.reject(error); 

เมื่อมีข้อผิดพลาดจากนั้น:

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

เมื่อเร็ว ๆ นี้ฉันพบสถานการณ์ที่ฉันต้องการโทร $ http ที่เรียกโดยการโหลดหน้าเว็บซ้ำ วิธีแก้ปัญหาที่ฉันใช้:

  1. ห่อหุ้มการเรียกทั้งสองลงในฟังก์ชัน
  2. ส่งการโทร $ http ครั้งที่สองเป็นการโทรกลับไปยังฟังก์ชันที่สอง
  3. เรียกใช้ฟังก์ชันที่สองใน apon .success

จะเกิดอะไรขึ้นถ้ามันเป็น for loop โดยมี n ครั้งเรียกเซิร์ฟเวอร์
มิทัน

2

นี่คือวิธีที่คุณสามารถทำแบบอะซิงโครนัสและจัดการสิ่งต่างๆได้เหมือนปกติ ทุกอย่างยังคงแบ่งปัน คุณได้รับการอ้างอิงถึงวัตถุที่คุณต้องการอัปเดต เมื่อใดก็ตามที่คุณอัปเดตสิ่งนั้นในบริการของคุณบริการนั้นจะได้รับการอัปเดตทั่วโลกโดยไม่ต้องดูหรือคืนสัญญา นี่เป็นสิ่งที่ดีมากเพราะคุณสามารถอัปเดตวัตถุที่อยู่ภายใต้บริการได้โดยไม่ต้องเชื่อมต่อ ใช้ Angular ตามที่ควรจะใช้ ฉันคิดว่าอาจเป็นความคิดที่ไม่ดีที่จะสร้าง $ http.get / post synchronous คุณจะได้รับความล่าช้าอย่างเห็นได้ชัดในสคริปต์

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

และบางแห่งในมุมมอง:

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

0

เนื่องจากการซิงค์ XHRกำลังเลิกใช้งานคุณจึงไม่ควรพึ่งพาสิ่งนั้น หากคุณจำเป็นต้องทำการร้องขอการโพสต์การซิงค์คุณสามารถใช้ตัวช่วยต่อไปนี้ในบริการเพื่อจำลองโพสต์แบบฟอร์ม

ทำงานโดยการสร้างฟอร์มที่มีอินพุตที่ซ่อนอยู่ซึ่งโพสต์ไปยัง 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();
}

ปรับเปลี่ยนตามความจำเป็นสำหรับความต้องการของคุณ


-4

สิ่งที่เกี่ยวกับการตัดสายของคุณด้วยPromise.all()วิธีการเช่น

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

อ้างอิงจากMDN

Promise.all รอการปฏิบัติตามทั้งหมด (หรือการปฏิเสธครั้งแรก)


คุณกำลังพูดถึงอะไร? คำถามไม่เกี่ยวข้องกับสัญญาหลายข้อ ...
Ovidiu Dolha

จะรอให้สัญญาหนึ่งข้อหรือมากกว่านั้นเสร็จสมบูรณ์!
Manjit Dosanjh

คุณเคยใช้สิ่งนี้เพื่อดูว่ามันทำงานอย่างไร? Promise.all จะคืนสัญญาอีกครั้ง แต่จะไม่เปลี่ยน async เป็นการโทรแบบซิงค์
Ovidiu Dolha

อืม ... ดูเหมือนว่าเอกสาร MDN อาจคลุมเครือ ... มันไม่ได้รอตามที่ระบุไว้ในเอกสาร
Manjit Dosanjh

ยินดีต้อนรับสู่ SO โปรดอ่านวิธีการตอบเพื่อให้คำตอบที่มีคุณภาพ
thewaywewere
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.