Як $ 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.
Марк Райкок

1
Асинхронність завжди краща, особливо коли у вас є обіцянки.
Ендрю Джослін

Багато разів можна уникати синхронних дзвінків. Подивіться, як працює $ ресурс stackoverflow.com/questions/11966252/… .
honzajde

3
@AndrewJoslin Асинхронний гірше, коли вам потрібна замовлена ​​доставка.
Штійн Ван Антверпен

Відповіді:


113

Наразі не. Якщо ви подивитеся на вихідний код (з цього моменту жовтень 2012 р.) , Ви побачите, що виклик до відкритого XHR насправді важко закодований, щоб бути асинхронним (третій параметр відповідає дійсності):

 xhr.open(method, url, true);

Вам потрібно буде написати власну службу, яка здійснювала синхронні дзвінки. Як правило, це не те, що ви зазвичай хочете робити через характер виконання JavaScript, ви в кінцевому підсумку блокуєте все інше.

... але .. якщо блокування всього іншого насправді бажано, можливо, ви повинні вивчити обіцянки та послугу $ q . Це дозволяє зачекати, поки буде виконано набір асинхронних дій, а потім виконати щось після того, як всі вони завершені. Я не знаю, у чому полягає ваша справа використання, але це, можливо, варто переглянути.

За межами цього, якщо ви збираєтеся згорнути свій власний, більш детальну інформацію про те , як зробити синхронні і асинхронні виклики Ajax можна знайти тут .

Я сподіваюся, що це корисно.


12
Чи можете ви, будь ласка, фрагмент коду, щоб досягти використання служби $ q. Я спробував багато варіантів, але він працює асинхронно.
Венкат

1
Є місця, де це може мати сенс, наприклад, коли користувач закриває браузер (onbeforeunload), якщо ви хочете зберегти, вам потрібно надіслати запит на синхронізацію, інший варіант - показати діалогове скасування, а потім повторно запустити вікно закрити?
Брауліо

2
@ Venkat: Я знаю, що це несвоєчасна відповідь, але, як я вже говорив у відповіді, дзвінок завжди буде "асинхронним". Ви просто повинні використовувати $ q, щоб змусити його чекати відповіді, а потім продовжувати свою логіку всередині .then(callback). що - щось на кшталт: doSomething(); $http.get('/a/thing').then(doEverythingElse);.
Бен Леш

3
Наступне відео допомогло мені вивчити обіцянки AngularJS Обіцянки з $ q
Ілля Палкін

1
@BenLesh Я не вдячний за час, який ви вводите, або час, коли хтось поміщає. Я вільний голосувати своєю відповіддю і сказати, що мені було б корисно, якби був приклад прикладу. Я побачив вашу відповідь, мені це не допомогло, тому я проголосував за неї і закрив вкладку, і повернувся до google, щоб спробувати знайти відповідь, яка мені більше допомогла. Це ще не кінець світу, коли хтось внизу озвучує вашу відповідь, і каже вам, як її можна покращити. Ви б хотіли, щоб я голосував, не залишаючи коментарів, чому? Просто чесно.
схема

12

Я працював з фабрикою, інтегрованою з автозаповненням google Maps, і обіцянками, сподіваюся, ви служите.

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

Вам потрібно замінити цей запит лише на autocompleteService на $ 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

Що робити, якщо це a для циклу, з n разів викликає сервер.
mithun

2

Ось спосіб це можна робити асинхронно та керувати речами, як зазвичай. Все ще ділиться. Ви отримуєте посилання на об’єкт, який потрібно оновити. Щоразу, коли ви оновлюєте це у вашій службі, воно оновлюється в усьому світі без необхідності дивитися або повертати обіцянку. Це дійсно приємно, оскільки ви можете оновити базовий об'єкт із сервісу, не маючи необхідності повторно вновлювати його. Використання Angular так, як це призначено для використання. Я думаю, що це, мабуть, погана ідея зробити $ http.get / post синхронним. Ви отримаєте помітну затримку в сценарії.

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 застаріла, краще не покладатися на це. Якщо вам потрібно виконати запит на синхронізацію POST, ви можете використовувати наступні помічники всередині сервісу для імітації форми форми.

Він працює, створюючи форму із прихованими входами, яка розміщується за вказаною 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 чекає всіх виконань (або першого відхилення)


про що ти говориш? питання не має нічого спільного з численними обіцянками ...
Овідіу Долха,

Він чекатиме завершення однієї чи кількох обіцянок!
Manjit Dosanjh

Ви використовували це, щоб побачити, як це працює? Promise.all поверне ще одну обіцянку, вона не перетворює асинхронізацію в синхронізовані дзвінки
Овідіу Долха,

Хм ... здається, що документація MDN може бути неоднозначною ... Це насправді НЕ ЗАЧЕКАЄ, як зазначено в їхній документації.
Manjit Dosanjh

Ласкаво просимо до SO. Будь ласка, прочитайте цю інструкцію щодо відповіді для надання якісної відповіді.
thewaywewere
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.