2012-12-18 26 views
162

は、別の要求が(例えば同じバックエンド、異なるパラメータを)起動された場合、その要求をキャンセルするための最も効果的な方法は何かAngularJS

$http.get("/backend/").success(callback); 

でAjaxリクエストを考えます。

+5

以下の回答は、実際にリクエスト自体をキャンセルしていません。 HTTPリクエストがブラウザを離れた後にキャンセルする方法はありません。下のすべての回答は単に何らかの形でリスナーに聞こえます。 HTTPリクエストは依然としてサーバーにヒットし、まだ処理されており、サーバーはまだレスポンスを送信しますが、クライアントがまだそのレスポンスを受けているかどうかはわかりません。 – Liam

+0

$ httpリクエストが経路変更でキャンセルされるhttp://www.freakyjolly.com/how-to-cancel-http-requests-in-angularjs-app/ –

答えて

283

この機能は、タイムアウトパラメータを経由してadded to the 1.1.5 releaseた:

var canceler = $q.defer(); 
$http.get('/someUrl', {timeout: canceler.promise}).success(successCallback); 
// later... 
canceler.resolve(); // Aborts the $http request if it isn't finished. 
+11

タイムアウトと約束によるマニュアルキャンセルの両方が必要な場合はどうすればよいですか? –

+12

@RamanChodźkaあなたは約束で両方を行うことができます。 JavaScriptのネイティブな 'setTimeout'関数やAngularの' $ timeout'サービスのいずれかを使って、一定時間後に約束をキャンセルするタイムアウトを設定することができます。 –

+0

ありがとう!私の以前の解決策は、AjaxリクエストにjQueryを使用していただけでした。 – mirichan

9

$httpで発行されたリクエストの取り消しは、現在のバージョンのAngularJSではサポートされていません。この機能を追加するにはpull request openedがありますが、このPRはまだレビューされていないため、AngularJSコアにするかどうかは不明です。

+0

PRが拒否された場合、OPが更新しました https:// github.com/angular/angular.js/pull/1836 –

+0

それも閉じられました。 – frapontillo

+0

これのバージョン(これは「https://github.com/angular/angular.js/commit/9f4f5937112655a9881d3281da8e72035bc8b180」)。まだ最終版を使用する構文を理解しようとしています。 PRsに使用例があることを願っています! :) – SimplGy

9

タイムアウトプロパティと$ HTTPアヤックス角度キャンセルすると、角度1.3.15では動作しません。 これが修正されるのを待つことができない人には、AngularでラップされたjQuery Ajaxソリューションを共有しています。

溶液は、2つのサービス含む:

  • のHTTPService(jQueryのAjaxの機能のラッパー)を、
  • PendingRequestsServiceここ

がPendingRequestsServiceサービスを行く(保留中/オープンAjaxリクエスト追跡):

(function (angular) { 
    'use strict'; 
    var app = angular.module('app'); 
    app.service('PendingRequestsService', ["$log", function ($log) {    
     var $this = this; 
     var pending = []; 
     $this.add = function (request) { 
      pending.push(request); 
     }; 
     $this.remove = function (request) { 
      pending = _.filter(pending, function (p) { 
       return p.url !== request; 
      }); 
     }; 
     $this.cancelAll = function() { 
      angular.forEach(pending, function (p) { 
       p.xhr.abort(); 
       p.deferred.reject(); 
      }); 
      pending.length = 0; 
     }; 
    }]);})(window.angular); 

のHTTPServiceサービス:

 (function (angular) { 
     'use strict'; 
     var app = angular.module('app'); 
     app.service('HttpService', ['$http', '$q', "$log", 'PendingRequestsService', function ($http, $q, $log, pendingRequests) { 
      this.post = function (url, params) { 
       var deferred = $q.defer(); 
       var xhr = $.ASI.callMethod({ 
        url: url, 
        data: params, 
        error: function() { 
         $log.log("ajax error"); 
        } 
       }); 
       pendingRequests.add({ 
        url: url, 
        xhr: xhr, 
        deferred: deferred 
       });    
       xhr.done(function (data, textStatus, jqXhr) {          
         deferred.resolve(data); 
        }) 
        .fail(function (jqXhr, textStatus, errorThrown) { 
         deferred.reject(errorThrown); 
        }).always(function (dataOrjqXhr, textStatus, jqXhrErrorThrown) { 
         //Once a request has failed or succeeded, remove it from the pending list 
         pendingRequests.remove(url); 
        }); 
       return deferred.promise; 
      } 
     }]); 
    })(window.angular); 

その後、あなたのサービスであなたがいますデータを読み込む場合は、$ http:

の代わりにHttpServiceを使用します。 あなたがデータをロードしたい
(function (angular) { 

    angular.module('app').service('dataService', ["HttpService", function (httpService) { 

     this.getResources = function (params) { 

      return httpService.post('/serverMethod', { param: params }); 

     }; 
    }]); 

})(window.angular); 

以降のコードでは:あなたはUI-ルータとstateChangeStart上の保留中の要求をキャンセルしたい場合は

(function (angular) { 

var app = angular.module('app'); 

app.controller('YourController', ["DataService", "PendingRequestsService", function (httpService, pendingRequestsService) { 

    dataService 
    .getResources(params) 
    .then(function (data) {  
    // do stuff  
    });  

    ... 

    // later that day cancel requests  
    pendingRequestsService.cancelAll(); 
}]); 

})(window.angular); 
6

、あなたはこのようなものを使用することができます。

//サービスで

   var deferred = $q.defer(); 
       var scope = this; 
       $http.get(URL, {timeout : deferred.promise, cancel : deferred}).success(function(data){ 
        //do something 
        deferred.resolve(dataUsage); 
       }).error(function(){ 
        deferred.reject(); 
       }); 
       return deferred.promise; 

// UIrouterの設定で

$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) { 
    //To cancel pending request when change state 
     angular.forEach($http.pendingRequests, function(request) { 
      if (request.cancel && request.timeout) { 
      request.cancel.resolve(); 
      } 
     }); 
    }); 
+0

これは私のために働いた - 非常にシンプルで、私はコールを選択していくつかのコールをキャンセルすることができるので、コールに名前を付ける別のものを追加しました –

+0

UIルータの設定は 'request.timeout'が存在するかどうかを知る必要がありますか? – trysis

2

これは、このコードがやっている

'use strict'; 
angular.module('admin') 
    .config(["$provide", function ($provide) { 

$provide.decorator('$http', ["$delegate", "$q", function ($delegate, $q) { 
    var getFn = $delegate.get; 
    var cancelerMap = {}; 

    function getCancelerKey(method, url) { 
    var formattedMethod = method.toLowerCase(); 
    var formattedUrl = encodeURI(url).toLowerCase().split("?")[0]; 
    return formattedMethod + "~" + formattedUrl; 
    } 

    $delegate.get = function() { 
    var cancelerKey, canceler, method; 
    var args = [].slice.call(arguments); 
    var url = args[0]; 
    var config = args[1] || {}; 
    if (config.timeout == null) { 
     method = "GET"; 
     cancelerKey = getCancelerKey(method, url); 
     canceler = $q.defer(); 
     cancelerMap[cancelerKey] = canceler; 
     config.timeout = canceler.promise; 
     args[1] = config; 
    } 
    return getFn.apply(null, args); 
    }; 

    $delegate.abort = function (request) { 
    console.log("aborting"); 
    var cancelerKey, canceler; 
    cancelerKey = getCancelerKey(request.method, request.url); 
    canceler = cancelerMap[cancelerKey]; 

    if (canceler != null) { 
     console.log("aborting", cancelerKey); 

     if (request.timeout != null && typeof request.timeout !== "number") { 

     canceler.resolve(); 
     delete cancelerMap[cancelerKey]; 
     } 
    } 
    }; 

    return $delegate; 
}]); 
    }]); 

WHAT ...次のようにabortメソッドを持つ$ HTTPサービスを飾ることで受け入れ答えを強化しますか?

要求をキャンセルするには、「約束」タイムアウトを設定する必要があります。 HTTP要求にタイムアウトが設定されていない場合、コードは「約束」タイムアウトを追加します。 (タイムアウトがすでに設定されている場合は、何も変更されません)。

しかし、約束を解決するには、「延期」に関するハンドルが必要です。 これでマップを使用するので、後で "遅延"を取り出すことができます。 abortメソッドを呼び出すと、マップから "deferred"が取得され、次にresolveメソッドを呼び出してhttp要求を取り消します。

これは誰かを助けることを望みます。

制限

$ http.getため現在、この唯一の作品がありますが、$ http.postためのコードを追加することができますので、

使い方...

にあなたがそれを使用することができますたとえば、次のような状態変化時に...

rootScope.$on('$stateChangeStart', function (event, toState, toParams) { 
    angular.forEach($http.pendingRequests, function (request) { 
     $http.abort(request); 
    }); 
    }); 
+0

私はいくつかのhttpリクエストを同時に起動するアプリを作っています。私は手動でそれらをすべて中止する必要があります。私はあなたのコードを試しましたが、それは最後のリクエストを中止します。それは以前に起こったのですか?どんな助けもありがとう。 –

+1

ここでのコードは、遅延オブジェクトへの参照の参照を維持するので、遅延オブジェクトが中止を要求されてから後で取り出すことができます。検索で重要なのはキー:値のペアです。値は遅延オブジェクトです。キーはリクエストメソッド/ urlに基づいて生成された文字列です。私はあなたが同じメソッド/ URLへの複数のリクエストを中止していると推測しています。このため、すべてのキーは同じで、マップ内では互いに上書きされます。 url /メソッドが同じであっても、独自の生成ロジックが生成されるように、キー生成ロジックを調整する必要があります。 – danday74

+1

上から続き...これはコード内のバグではなく、コードは複数のリクエストを中止します...しかし、コードは同じHTTPメソッドを使用して同じURLへの複数のリクエストを中止することを決して意味しませんでした...しかし、もしあなたがそれをかなり簡単に動かすことができるはずです。 – danday74

5

何らかの理由でconfig.timeoutが動作しません。私はこのアプローチを使用:

let cancelRequest = $q.defer(); 
 
let cancelPromise = cancelRequest.promise; 
 

 
let httpPromise = $http.get(...); 
 

 
$q.race({ cancelPromise, httpPromise }) 
 
    .then(function (result) { 
 
... 
 
});

とcancelRequest.resolve()キャンセルします。実際にはリクエストをキャンセルするわけではありませんが、少なくとも不要な応答はありません。

これが役に立ちます。

+0

SyntaxError '{cancelPromise、httpPromise}'が見つかりましたか? – Mephiztopheles

+0

これはES6の構文です、あなたは{c:cancelPromise、h:httpPromise}を試すことができます –

+0

私は、オブジェクトshortinitializerを参照してください – Mephiztopheles

0

ここでは、複数の要求を処理し、エラーブロック内のエラーを抑制するコールバックでキャンセルされたステータスをチェックするバージョンです。 (活字体で)

コントローラレベル:私のHTTP GETで

requests = new Map<string, ng.IDeferred<{}>>(); 

getSomething(): void { 
     let url = '/api/someaction'; 
     this.cancel(url); // cancel if this url is in progress 

     var req = this.$q.defer(); 
     this.requests.set(url, req); 
     let config: ng.IRequestShortcutConfig = { 
      params: { id: someId} 
      , timeout: req.promise // <--- promise to trigger cancellation 
     }; 

     this.$http.post(url, this.getPayload(), config).then(
      promiseValue => this.updateEditor(promiseValue.data as IEditor), 
      reason => { 
       // if legitimate exception, show error in UI 
       if (!this.isCancelled(req)) { 
        this.showError(url, reason) 
       } 
      }, 
     ).finally(() => { }); 
    } 

ヘルパーメソッド

cancel(url: string) { 
     this.requests.forEach((req,key) => { 
      if (key == url) 
       req.resolve('cancelled'); 
     }); 
     this.requests.delete(url); 
    } 

    isCancelled(req: ng.IDeferred<{}>) { 
     var p = req.promise as any; // as any because typings are missing $$state 
     return p.$$state && p.$$state.value == 'cancelled'; 
    } 

が、今私はそれことがわかり、ネットワーク]タブを見てbeatuifully動作します。私は方法を4回呼んで、最後のものだけが通過した。

enter image description here

関連する問題