Angular.js服务/工厂具有可重用数据

时间:2022-10-29 13:42:20

I'm trying to build a service or factory that allows me to load date through some API calls. Most of this data will need to be re-used, so essentially I only want to make one API call, the next time I need that data, it should just return it.

我正在尝试构建一个允许我通过一些API调用加载日期的服务或工厂。大多数这些数据都需要重复使用,所以基本上我只想进行一次API调用,下次当我需要这些数据时,它应该只返回它。

Now whenever I make an API call, and before it's finished I make the same call, I want the second call to wait until the first one is completed.

现在每当我进行API调用时,在它完成之前我都进行相同的调用,我希望第二个调用等到第一个调用完成。

Essentially when I do this:

基本上当我这样做时:

dataService.getMenu() // Make API call
dataService.getMenu() // Wait for the first API call to be completed and return that data

// Somewhere else
dataService.getMenu() // Return data as API call was already made

My factory looks like this:

我的工厂看起来像这样:

(function() {
    var app = angular.module('dataService', []);
    app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = [],
            jobs = [];

        return {
            getMenu: function() {
                var deferred = $q.defer();

                console.log(links);

                if(links.length > 0) {
                    deferred.resolve(links);
                } else {
                    $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) {
                        links = data;

                        deferred.resolve(data);
                    })
                }

                return deferred.promise;
            }
        }
    }])
})();

3 个解决方案

#1


2  

Just move the declaration for defer outside the getMenu function, into factory

只需将getMenu函数之外的延迟声明移到工厂即可

 app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = [],
            jobs = [],
            deferredMenu = $q.defer();

Now use deferredMenu promise in side your getMenu call.

现在在getMenu调用中使用deferredMenu promise。

getMenu: function() {
                if(links.length > 0) {
                    deferredMenu.resolve(links);
                } else {
                    $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) {
                        links = data;

                        deferredMenu.resolve(data);
                    })
                }

                return deferredMenu.promise;
}

#2


0  

You can make use of the cache config in the $http service to do this for you. As stated in the $http caching documentation:

您可以使用$ http服务中的缓存配置为您执行此操作。如$ http缓存文档中所述:

To enable caching, set the request configuration cache property to true (to use default cache) or to a custom cache object (built with $cacheFactory). When the cache is enabled, $http stores the response from the server in the specified cache. The next time the same request is made, the response is served from the cache without sending a request to the server

要启用缓存,请将请求配置缓存属性设置为true(使用默认缓存)或自定义缓存对象(使用$ cacheFactory构建)。启用缓存后,$ http将来自服务器的响应存储在指定的缓存中。下次发出相同请求时,将从缓存提供响应,而不向服务器发送请求

Note that even if the response is served from cache, delivery of the data is asynchronous in the same way that real requests are.

请注意,即使响应是从缓存提供的,数据的传递也是异步的,与实际请求的方式相同。

If there are multiple GET requests for the same URL that should be cached using the same cache, but the cache is not populated yet, only one request to the server will be made and the remaining requests will be fulfilled using the response from the first request.

如果对应使用相同缓存缓存的同一URL存在多个GET请求,但尚未填充缓存,则只会向服务器发出一个请求,并使用第一个请求的响应来完成剩余的请求。

The statement above fits the requirements stated in your question. Furthermore, I have omitted the $q service since $http methods already provide promises that you need, you simply need to have the data object in your response using the then() $q service method.

上述声明符合您问题中的要求。此外,我省略了$ q服务,因为$ http方法已经提供了你需要的promises,你只需要使用then()$ q服务方法在响应中拥有数据对象。

(function() {
    var app = angular.module('dataService', []);
    app.factory('dataService', ['$http', function($http) {
        return {
            getMenu: function() {
               return $http.get('../server/api.php?ajax=true&action=getCats', {cache: true})
                  .then(function(response) {
                    return response.data;
                  });
            }
        };
    }])
})();

#3


0  

I know I am bit late to answer this question, but I faced the same problem and came up with a solution after lot of research.

我知道我回答这个问题有点迟了,但我遇到了同样的问题,经过大量的研究后提出了解决方案。

The approach to achieve the above requirement is to use queuing of calls.

实现上述要求的方法是使用呼叫排队。

The following are the steps for the same:

以下是相同的步骤:

  1. For each call, create a promise and add the promise to the queue. Return defer.promise for each call.
  2. 对于每个调用,创建一个promise并将promise添加到队列中。为每次通话返回defer.promise。

  3. For the first item in the queue, call a function which will bring your api and in the response of the API, set a parameter say IsDataPresent = true(initially it was false).
  4. 对于队列中的第一项,调用一个将带来api的函数,并在API的响应中设置一个参数,如IsDataPresent = true(最初为假)。

  5. Resolve the promise of the first call and set received data in a local variable. Execute the function for next call in the queue, but first check for IsDataPresent=== true, if true then resolve the next call's promise with the data of local variable.
  6. 解决第一次调用的承诺并在本地变量中设置接收的数据。执行队列中下一次调用的函数,但首先检查IsDataPresent === true,如果为true,则使用局部变量的数据解析下一个调用的promise。

See the code below for the same:

请参阅以下代码:

app.factory('dataService', ['$http', '$q', function($http, $q) {
    var links = '',
        jobs = [],
        isDataAlreadyPresent = false;

    var getMenuCall = function() { 
        var call = jobs[0]; // Retrieve first promise from the queue
        if (isDataAlreadyPresent) { // This will be false for first call and true for all other
            call.defer.resolve(links);
        } else {
            $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { //Get API data
                isDataAlreadyPresent = true; //Set parameter to true
                links = data; // Set local variable to received data
                call.defer.resolve.resolve(data); // Resolve the first promise
                jobs.shift(); // Remove first item from the queue
                if (jobs.length > 0) {
                    getMenuCall(); // Execute the function for next call's promise in the queue. This time isDataAlreadyPresent== true will be true so it's promise will be resolved by the links data thus avoiding extra call. 
                }
            });
        }

        return deferredMenu.promise;
    };

    return {
        getMenu: function() {
            var defer = $q.defer(); // Create promise for the call
            jobs.push({
                defer: defer // Push each call's promise to the queue
            });
            if (jobs.length === 1) { // For the first call make call above function which will make API call
                getMenuCall();
            }
            return defer.promise; // Defer promise for the time being
        }
    }
}]);

#1


2  

Just move the declaration for defer outside the getMenu function, into factory

只需将getMenu函数之外的延迟声明移到工厂即可

 app.factory('dataService', ['$http', '$q', function($http, $q) {
        var links = [],
            jobs = [],
            deferredMenu = $q.defer();

Now use deferredMenu promise in side your getMenu call.

现在在getMenu调用中使用deferredMenu promise。

getMenu: function() {
                if(links.length > 0) {
                    deferredMenu.resolve(links);
                } else {
                    $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) {
                        links = data;

                        deferredMenu.resolve(data);
                    })
                }

                return deferredMenu.promise;
}

#2


0  

You can make use of the cache config in the $http service to do this for you. As stated in the $http caching documentation:

您可以使用$ http服务中的缓存配置为您执行此操作。如$ http缓存文档中所述:

To enable caching, set the request configuration cache property to true (to use default cache) or to a custom cache object (built with $cacheFactory). When the cache is enabled, $http stores the response from the server in the specified cache. The next time the same request is made, the response is served from the cache without sending a request to the server

要启用缓存,请将请求配置缓存属性设置为true(使用默认缓存)或自定义缓存对象(使用$ cacheFactory构建)。启用缓存后,$ http将来自服务器的响应存储在指定的缓存中。下次发出相同请求时,将从缓存提供响应,而不向服务器发送请求

Note that even if the response is served from cache, delivery of the data is asynchronous in the same way that real requests are.

请注意,即使响应是从缓存提供的,数据的传递也是异步的,与实际请求的方式相同。

If there are multiple GET requests for the same URL that should be cached using the same cache, but the cache is not populated yet, only one request to the server will be made and the remaining requests will be fulfilled using the response from the first request.

如果对应使用相同缓存缓存的同一URL存在多个GET请求,但尚未填充缓存,则只会向服务器发出一个请求,并使用第一个请求的响应来完成剩余的请求。

The statement above fits the requirements stated in your question. Furthermore, I have omitted the $q service since $http methods already provide promises that you need, you simply need to have the data object in your response using the then() $q service method.

上述声明符合您问题中的要求。此外,我省略了$ q服务,因为$ http方法已经提供了你需要的promises,你只需要使用then()$ q服务方法在响应中拥有数据对象。

(function() {
    var app = angular.module('dataService', []);
    app.factory('dataService', ['$http', function($http) {
        return {
            getMenu: function() {
               return $http.get('../server/api.php?ajax=true&action=getCats', {cache: true})
                  .then(function(response) {
                    return response.data;
                  });
            }
        };
    }])
})();

#3


0  

I know I am bit late to answer this question, but I faced the same problem and came up with a solution after lot of research.

我知道我回答这个问题有点迟了,但我遇到了同样的问题,经过大量的研究后提出了解决方案。

The approach to achieve the above requirement is to use queuing of calls.

实现上述要求的方法是使用呼叫排队。

The following are the steps for the same:

以下是相同的步骤:

  1. For each call, create a promise and add the promise to the queue. Return defer.promise for each call.
  2. 对于每个调用,创建一个promise并将promise添加到队列中。为每次通话返回defer.promise。

  3. For the first item in the queue, call a function which will bring your api and in the response of the API, set a parameter say IsDataPresent = true(initially it was false).
  4. 对于队列中的第一项,调用一个将带来api的函数,并在API的响应中设置一个参数,如IsDataPresent = true(最初为假)。

  5. Resolve the promise of the first call and set received data in a local variable. Execute the function for next call in the queue, but first check for IsDataPresent=== true, if true then resolve the next call's promise with the data of local variable.
  6. 解决第一次调用的承诺并在本地变量中设置接收的数据。执行队列中下一次调用的函数,但首先检查IsDataPresent === true,如果为true,则使用局部变量的数据解析下一个调用的promise。

See the code below for the same:

请参阅以下代码:

app.factory('dataService', ['$http', '$q', function($http, $q) {
    var links = '',
        jobs = [],
        isDataAlreadyPresent = false;

    var getMenuCall = function() { 
        var call = jobs[0]; // Retrieve first promise from the queue
        if (isDataAlreadyPresent) { // This will be false for first call and true for all other
            call.defer.resolve(links);
        } else {
            $http.get('../server/api.php?ajax=true&action=getCats').success(function(data) { //Get API data
                isDataAlreadyPresent = true; //Set parameter to true
                links = data; // Set local variable to received data
                call.defer.resolve.resolve(data); // Resolve the first promise
                jobs.shift(); // Remove first item from the queue
                if (jobs.length > 0) {
                    getMenuCall(); // Execute the function for next call's promise in the queue. This time isDataAlreadyPresent== true will be true so it's promise will be resolved by the links data thus avoiding extra call. 
                }
            });
        }

        return deferredMenu.promise;
    };

    return {
        getMenu: function() {
            var defer = $q.defer(); // Create promise for the call
            jobs.push({
                defer: defer // Push each call's promise to the queue
            });
            if (jobs.length === 1) { // For the first call make call above function which will make API call
                getMenuCall();
            }
            return defer.promise; // Defer promise for the time being
        }
    }
}]);