AngularJSで$ http同期呼び出しする方法


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

イベント、$ watch、サーバー側でのプリロード、$ httpから返されたpromiseの使用など、非同期動作の処理方法に関するいくつかのアイデアについては、groups.google.com / d / topic / angular / qagzXXhS_VI / discussionも参照してください。
Mark Rajcok、2012年

1
特に約束がある場合は、非同期の方が常に優れています。
Andrew Joslin、2013

多くの場合、同期呼び出しを回避できます。$ resourceがどのように機能するかをご覧ください。stackoverflow.com/questions/11966252/…
honzajde 2013

3
@AndrewJoslin Asynchronousは、注文した配送が必要な場合により悪くなります。
Stijn Van Antwerpen

回答:


113

現在はありません。あなたがいる場合(時間2012年10月のこの時点から)のソースコードを見て、あなたはXHRオープンへの呼び出しが実際に(三番目のパラメータがtrueである)非同期にハードコーディングされていることがわかります。

 xhr.open(method, url, true);

同期呼び出しを行う独自のサービスを作成する必要があります。一般的に、JavaScript実行の性質上、他のすべてをブロックすることになるため、通常はこれを実行する必要はありません。

...しかし、他のすべてをブロックすることが実際に必要な場合は、おそらくpromiseと$ qサービスを調べる必要があります。一連の非同期アクションが完了するまで待機し、すべてが完了したら何かを実行できます。私はあなたのユースケースが何であるかわかりませんが、それは一見の価値があるかもしれません。

それ以外に、独自にロールバックする場合、同期および非同期のajax呼び出しを行う方法の詳細については、こちらを参照してください

お役に立てば幸いです。


12
$ qサービスを使用してスニペットをコーディングしてください。私は多くのオプションを試しましたが、非同期に動作しています。
Venkat 2014年

1
たとえば、ユーザーがブラウザーを閉じたとき(onbeforeunload)、同期要求を送信する必要がある場合は、ダイアログキャンセルを表示してからウィンドウを閉じて再起動するという別のオプションがあります。
Braulio 2014年

2
@ベンカット:これは返信が遅いことはわかっていますが、回答で述べたように、呼び出しは常に「非同期」になります。$ qを使用して応答を待機させ、ロジックを内部で続行するだけです。.then(callback)。以下のようなもの:doSomething(); $http.get('/a/thing').then(doEverythingElse);
Ben Lesh


1
@BenLesh私はあなたが入れた時間、または誰かが入れた時間に感謝しているわけではありません。私は自由にあなたの回答に反対票を投じ、例が提供されたなら私に役立つだろうと言います。私はあなたの答えを見ました、それは私を助けませんでした、それで私はそれを反対票を投じてタブを閉じて、そして私にもっと役立つ答えを見つけようとしてグーグルに戻りました。誰かがあなたの回答に反対票を投じ、それがどのように改善できるかを伝えるのは、世界の終わりではありません。理由についてコメントせずに私が反対票を投じたほうがいいですか。ただ正直だ。
回路

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つの呼び出しを関数にカプセル化する
  2. 2番目の$ http呼び出しを2番目の関数へのコールバックとして渡します
  3. apon .successの2番目の関数を呼び出す

n回の呼び出しサーバーを使用したforループの場合はどうでしょうか。
mithun 2016年

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はすべてのフルフィルメント(または最初の拒否)を待機します


あなたは何について話していますか?質問は複数の約束とは何の関係もありません...
Ovidiu Dolha 2017年

1つ以上の約束が完了するのを待ちます!
Manjit Dosanjh

これを使用して、それがどのように機能するかを確認しましたか?Promise.allは別の約束を返しますが、非同期を同期呼び出しに変換しません
Ovidiu Dolha

うーん... MDNのドキュメントがあいまいなようです...実際には、ドキュメントに記載されているように待機しません。
Manjit Dosanjh

SOへようこそ。質の高い回答を提供するにはこちらの回答方法をお読みください。
2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.