中止AngularJS $ http请求,深度嵌套在多个服务调用中

时间:2014-06-25 07:41:53

标签: javascript angularjs promise q angularjs-http

我使用延迟承诺中止$http请求(如this帖子中所述)。

但是,我不确定如何传播在根级别添加的abort()函数。每次我在promise上调用.then()时,都会返回一个新的promise(没有abort()函数)。

请参阅下面的示例。我有一个控制器调用服务,然后调用另一个服务(发出$http请求的地方)。该示例不起作用,因为promise中的MyControllerrestService中返回的abort()不同,因此没有名为abort()的函数。< / p>

  1. 如何传播MyController功能,使其在then()中可用?
  2. 我可以一直返回原始承诺,只需在其上调用then()(如底部示例中所示)? app.controller("MyController", function($scope, dataService) { var promise; $scope.loadAndAbortData = function () { promise = dataService.getData().then(updateUI); }; $scope.abort = function () { if (promise) { promise.abort(); } }; } 来电会怎么样?它们是否仍被同步调用&#34;?
  3. 控制器

    app.service("dataService", function(restService) {
        var service = {
            getData: function () {
                return restService.get().then(function (response) {
                    var modifiedData = modifyData(response.data);
                    return modifiedData;
                }, function (response) {
                    handleError(response.data);
                    $q.reject(response.data);
                });               
            };
        };
    
        return service;
    }
    

    的DataService

    app.service("restService", function($http, $q) {
        var service = {
            get: function () {
                var deferredAbort = $q.defer();
                var request = $http.get(url, { timeout: deferredAbort.promise } );
    
                promise.abort = function () {
                    deferredAbort.resolve();
                }
    
                return promise;
            };
        };
    
        return service;
    }
    

    RestService:

    app.controller("MyController", function($scope, dataService) {
        var promise;
    
        $scope.loadAndAbortData = function () {
            promise = dataService.getData();
            promise.then(updateUI);
        };
    
        $scope.abort = function () {
            if (promise) {
                promise.abort();
            }
        };
    }
    
    app.service("dataService", function(restService) {
        var service = {
            getData: function () {
                var promise = restService.get();
                promise.then(function (response) {
                    var modifiedData = modifyData(response.data);
                    return modifiedData;
                }, function (response) {
                    handleError(response.data);
                    $q.reject(response.data);
                });
    
                return promise;           
            };
        };
    
        return service;
    }
    

    这是DataService和MyController的解决方案吗?

    {{1}}

2 个答案:

答案 0 :(得分:1)

乔尔,我对此并不是百分之百确定,但我会根据你已经尝试过的事情试一试。

整个事情似乎取决于在RestService中,在timeout: Promise配置映射中提供可解析的$http()选项,并以某种方式返回带有.abort()方法的promise以及其标准方法;然后确保.abort()方法&#34;继承&#34;通过DataService,进入Controller。为实现这一目标,需要一些小技巧。

我猜你可以这样做:

//RestService:
app.service("restService", function($http, $q) {
    return {
        get: function () {
            var dfrd = $q.defer(),
                promise = $http.get(url, {
                    timeout: dfrd.promise
                });

            //attach the deferred's resolve method as promise's abort method
            promise.abort = dfrd.resolve.bind(dfrd);//(or with an explicit function wrapper, as in the question)

            return promise;
        },
    };
}

同样,在DataService中,返回的promise需要一个abort方法,该方法再次引用回RestService中的dfrd.resolve方法。

//DataService
app.service("dataService", function(restService) {
    return {
        getData: function () {
            //Here, the natural syntax would be `return restService.get().then(...)`,
            //however a reference to intermediate `restService.get()` is required such that its special `.abort()` method can be attached as the `.abort()` method of the eventually returned promise.
            var promise = restService.get();
            var promise_ = promise.then(function(response) {
                var modifiedData = modifyData(response.data);
                return modifiedData;
            }, function (response) {
                handleError(response.data);
                $q.reject(response.data);
            });
            promise_.abort = promise.abort;//attach promise's abort method as promise_'s abort method.
            return promise_;
        }
    };
});

因此,dataService.getData()应该使用abort方法向Controller传递承诺。

//Controller
app.controller("MyController", function($scope, dataService) {
    var promise;

    //Here, play the same trick as in the DataService - 
    //ie. assign the intermediate promise rather than the output of the full chain.
    $scope.loadAndAbortData = function () {
        promise = dataService.getData();
        promise.then(updateUI);
    };
    $scope.abort = function () {
        if (promise && promise.abort) {
            promise.abort();
        }
    };
}

答案 1 :(得分:0)

我最终找到了一个“覆盖”承诺的then()函数的解决方案,以便abort()函数将遍历所有图层,而与then()/catch()/finally()的次数无关。在承诺上被召唤。

如果有人找到更好的解决方案或者看到这个问题存在缺陷,我真的很感激一些意见。

app.service("restService", function($http, $q) {

    function createAbortablePromise(promise, deferredAbort) {
        promise.abort = function () {
            deferredAbort.resolve();
        };

        // A problem with adding the abort function to the promise is that as soon as someone
        // calls then() on this promise (somewhere else in our application), another new promise
        // is returned. This new promise, will not have the abort() function. We can solve this
        // by "overriding" then() recursively.
        var originalThen = promise.then;
        promise.then = function (callback, errback, progressback) {
            // Invoke the original then(). It will return a new promise
            var newPromise = originalThen(callback, errback, progressback);

            // This new promise needs an abort function as well.
            newPromise = createAbortablePromise(newPromise, deferredAbort);

            return newPromise;
        };

        return promise;
    }

    var service = {
        get: function () {
            var deferredAbort = $q.defer();
            var request = $http.get(url, { timeout: deferredAbort.promise } );

            promise.abort = function () {
                deferredAbort.resolve();
            }

            promise = createAbortablePromise(promise, deferredAbort);

            return promise;
        };
    };

    return service;
}