如何取消 $resource 请求 [英] How to cancel $resource requests

查看:21
本文介绍了如何取消 $resource 请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想弄清楚如何使用 $resource 的超时属性来动态取消挂起的请求.理想情况下,我只想能够取消具有某些属性的请求(基于发送的参数),但这似乎是不可能的.与此同时,我只是想取消所有待处理的请求,然后重置超时承诺以允许新请求.

I'm trying to figure out how to use the timeout property of a $resource to dynamically cancel pending requests. Ideally, I'd like to just be able to cancel requests with certain attributes (based on the params sent), but it seems this may not be possible. In the meantime, I'm just trying to cancel all pending requests, and then resetting the timeout promise to allow new requests.

问题似乎是 $resource 配置只允许超时值的单个静态承诺.如果我进行单独的 $http 调用,我如何做到这一点是有道理的,因为我可以只为超时传递新的承诺,但是这如何适用于 $resource?我在这里设置了一个示例 plunker:http://plnkr.co/edit/PP2tqDYXh1NAOU3yqCwP?p=预览

The issue seems to be that the $resource configuration only allows a single, static promise for the timeout value. It makes sense how I could do this if I was making individual $http calls, since I could just pass in new promises for the timeout, but how can this work for a $resource? I have set up an example plunker here: http://plnkr.co/edit/PP2tqDYXh1NAOU3yqCwP?p=preview

这是我的控制器代码:

app.controller('MainCtrl', function($scope, $timeout, $q, $resource) {
  $scope.canceller = $q.defer();
  $scope.pending = 0;
  $scope.actions = [];
  var API = $resource(
    'index.html', {}, {
      get: {
        method: 'GET',
        timeout: $scope.canceller.promise
      }
    }
  )

  $scope.fetchData = function() {
    if ($scope.pending) {
      $scope.abortPending();
    }
    $scope.pending = 1;
    $scope.actions.push('request');
    API.get({}, function() {
      $scope.actions.push('completed');
      $scope.pending = 0;
    }, function() {
      $scope.actions.push('aborted');
    });
  }

  $scope.abortPending = function() {
    $scope.canceller.resolve();
    $scope.canceller = $q.defer();
  }
});

现在,取消器在有待处理请求时工作,但我似乎无法重置它 - 一旦一个请求被中止,所有未来的请求也将被中止.

Right now, the canceller works when there is a pending request, but I don't seem to be able to reset it - once one request is aborted, all future requests will be aborted as well.

我确定我遗漏了一些东西,因为能够取消待处理的请求似乎是大多数网络应用程序(至少我构建的)的一个非常重要的功能.

I'm sure I'm missing something, since being able to cancel pending requests seems like a pretty crucial feature of most web applications (at least that I've built).

谢谢

推荐答案

Answer by Gecko IT 对我有用,但是我必须进行一些修改才能:

Answer by Gecko IT works for me, but I had to make some modifications in order to:

  • 启用多次取消资源ajax调用,无需重新创建资源
  • 使资源向后兼容 - 这意味着除资源工厂外无需更改任何应用程序(控制器)代码
  • 使代码符合 JSLint

这是完整的服务工厂实现(你只需要输入正确的模块名称):

This is complete service factory implementation (you just need to put proper module name):

'use strict';

/**
 * ResourceFactory creates cancelable resources.
 * Work based on: https://stackoverflow.com/a/25448672/1677187
 * which is based on: https://developer.rackspace.com/blog/cancelling-ajax-requests-in-angularjs-applications/
 */
/* global array */
angular.module('module_name').factory('ResourceFactory', ['$q', '$resource',
    function($q, $resource) {

        function abortablePromiseWrap(promise, deferred, outstanding) {
            promise.then(function() {
                deferred.resolve.apply(deferred, arguments);
            });

            promise.catch(function() {
                deferred.reject.apply(deferred, arguments);
            });

            /**
             * Remove from the outstanding array
             * on abort when deferred is rejected
             * and/or promise is resolved/rejected.
             */
            deferred.promise.finally(function() {
                array.remove(outstanding, deferred);
            });
            outstanding.push(deferred);
        }

        function createResource(url, options, actions) {
            var resource;
            var outstanding = [];
            actions = actions || {};

            Object.keys(actions).forEach(function(action) {
                var canceller = $q.defer();
                actions[action].timeout = canceller.promise;
                actions[action].Canceller = canceller;
            });

            resource = $resource(url, options, actions);

            Object.keys(actions).forEach(function(action) {
                var method = resource[action];

                resource[action] = function() {
                    var deferred = $q.defer(),
                    promise = method.apply(null, arguments).$promise;

                    abortablePromiseWrap(promise, deferred, outstanding);

                    return {
                        $promise: deferred.promise,

                        abort: function() {
                            deferred.reject('Aborted');
                        },
                        cancel: function() {
                            actions[action].Canceller.resolve('Call cancelled');

                            // Recreate canceler so that request can be executed again
                            var canceller = $q.defer();
                            actions[action].timeout = canceller.promise;
                            actions[action].Canceller = canceller;
                        }
                    };
                };
            });

            /**
             * Abort all the outstanding requests on
             * this $resource. Calls promise.reject() on outstanding [].
             */
            resource.abortAll = function() {
                for (var i = 0; i < outstanding.length; i++) {
                    outstanding[i].reject('Aborted all');
                }
                outstanding = [];
            };

            return resource;
        }

        return {
            createResource: function (url, options, actions) {
                return createResource(url, options, actions);
            }
        };
    }
]);

用法与 Gecko IT 示例中的相同.服务工厂:

Usage is the same as in Gecko IT example. Service factory:

'use strict';

angular.module('module_name').factory('YourResourceServiceName', ['ResourceFactory', function(ResourceFactory) {
    return ResourceFactory.createResource('some/api/path/:id', { id: '@id' }, {
        create: {
            method: 'POST'
        },
        update: {
            method: 'PUT'
        }
    });
}]);

在控制器中的使用(向后兼容):

Usage in controller (backward compatible):

var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {
    // Successfully obtained data
}, function error(httpResponse) {
    if (httpResponse.status === 0 && httpResponse.data === null) { 
        // Request has been canceled
    } else { 
        // Server error 
    }
});
result.cancel(); // Cancels XHR request

替代方法:

var result = YourResourceServiceName.create(data);
result.$promise.then(function success(data, responseHeaders) {       
    // Successfully obtained data
}).catch(function (httpResponse) {
    if (httpResponse.status === 0 && httpResponse.data === null) { 
        // Request has been canceled
    } else { 
        // Server error 
    }
});
result.cancel(); // Cancels XHR request

进一步改进:

  • 我不喜欢检查请求是否已取消.更好的方法是在请求被取消时附加属性 httpResponse.isCanceled,类似中止.
  • I don't like checking if request has been canceled. Better approach would be to attach attribute httpResponse.isCanceled when request is canceled, and similar for aborting.

这篇关于如何取消 $resource 请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆