Как выполнить синхронный вызов $ 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
Асинхронность всегда лучше, особенно когда у вас есть обещания.
Эндрю Джослин

Во многих случаях вы можете избежать синхронных вызовов. Посмотрите, как работает $ resource stackoverflow.com/questions/11966252/… .
honzajde

3
@AndrewJoslin Асинхронный режим хуже, когда вам нужна заказанная доставка.
Stijn Van Antwerpen,

Ответы:


113

Не сейчас. Если вы посмотрите на исходный код (с этого момента времени, октябрь 2012 г.) , вы увидите, что вызов XHR open на самом деле жестко запрограммирован как асинхронный (третий параметр истинен):

 xhr.open(method, url, true);

Вам нужно будет написать свою собственную службу, выполняющую синхронные вызовы. Обычно это не то, что вы обычно хотите делать из-за характера выполнения JavaScript, вы в конечном итоге заблокируете все остальное.

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

Кроме того, если вы собираетесь развернуть свой собственный, дополнительную информацию о том, как выполнять синхронные и асинхронные вызовы ajax, можно найти здесь .

Я надеюсь, что это полезно.


12
Не могли бы вы получить фрагмент кода для использования службы $ q. Я перепробовал много вариантов, но работает асинхронно.
Venkat

1
Есть места, где это может иметь смысл, например, когда пользователь закрывает браузер (onbeforeunload), если вы хотите сохранить, вам нужно отправить запрос на синхронизацию, другой вариант - показать диалоговое окно отмены, а затем перезапустить окно закрытия?
Браулио

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

3
Следующее видео помогло мне в изучении обещаний AngularJS Promises с $ q
Илья Палкин

1
@BenLesh Я благодарен за время, которое вы вложили, или за время, которое кто-то вкладывает. Я могу проголосовать против вашего ответа и сказать, что для меня было бы полезно, если бы был приведен пример. Я видел ваш ответ, он мне не помог, поэтому я проголосовал против, закрыл вкладку и вернулся в Google, чтобы попытаться найти ответ, который был бы более полезным для меня. Это не конец света, когда кто-то отрицает ваш ответ и рассказывает, как его можно улучшить. Вы бы предпочли, чтобы я проголосовал против, не комментируя почему? Просто быть честным.
Схема

12

Я работал с фабрикой, интегрированной с автозаполнением карт Google и сделанными обещаниями, надеюсь, вы обслужите.

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

Что, если это цикл for, в котором 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){....}])

По данным МДН

Promise.all ждет всех выполнений (или первого отказа)


О чем ты говоришь? вопрос не имеет ничего общего с множественными обещаниями ...
Овидиу

Он будет ждать выполнения одного или нескольких обещаний!
Manjit Dosanjh

Вы использовали это, чтобы увидеть, как это работает? Promise.all вернет другое обещание, оно не преобразует асинхронные вызовы в синхронизирующие вызовы,
Овидиу

Хм ... кажется, что документация MDN может быть двусмысленной ... На самом деле она не WAIT, как указано в их документации.
Manjit Dosanjh

Добро пожаловать в ТАК. Пожалуйста, прочтите это руководство, чтобы получить качественный ответ.
thewaywewere
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.