【问题标题】:promise resolution seen in multiple controllers在多个控制器中看到的承诺解决方案
【发布时间】:2015-12-02 21:12:48
【问题描述】:

我希望两个不同的控制器在服务中解决某些承诺后运行不同的功能(我不希望该服务在每次控制器需要数据时都发出 http 请求,我只想要一个 http 请求)。

我有一个服务,它发出请求并得到一个承诺。我希望 controller1 看到这个分辨率,然后运行一些代码。然后我希望控制器 2 也看到这个承诺解决并运行一些代码(基本上是多个 then() 方法,它们在同一个承诺上运行但来自不同的文件)。我该怎么做?

我看到的所有示例在某个承诺解决后都有一个控制器运行代码,但没有多个控制器监听同一个承诺以解决。

这是我从本文中借用的一些代码(我会添加一个“母控制器”来说明我的示例,我不希望子服务进行两次 http 调用):http://andyshora.com/promises-angularjs-explained-as-cartoon.html

儿子服务

app.factory('SonService', function ($http, $q) {
    return {
        getWeather: function() {
            // the $http API is based on the deferred/promise APIs exposed by the $q service
            // so it returns a promise for us by default
            return $http.get('http://fishing-weather-api.com/sunday/afternoon')
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        return response.data;
                    } else {
                        // invalid response
                        return $q.reject(response.data);
                    }

                }, function(response) {
                    // something went wrong
                    return $q.reject(response.data);
                });
        }
    };
});

父亲控制器:

 // function somewhere in father-controller.js
        var makePromiseWithSon = function() {
            // This service's function returns a promise, but we'll deal with that shortly
            SonService.getWeather()
                // then() called when son gets back
                .then(function(data) {
                    // promise fulfilled
                    if (data.forecast==='good') {
                        prepareFishingTrip();
                    } else {
                        prepareSundayRoastDinner();
                    }
                }, function(error) {
                    // promise rejected, could log the error with: console.log('error', error);
                    prepareSundayRoastDinner();
                });
        };

主控制器:

var makePromiseWithSon = function() {
            SonService.getWeather()
                // then() called when son gets back
                .then(function(data) {
                    // promise fulfilled
                    if (data.forecast==='good') {
                        workInTheGarden();
                    } else {
                        sweepTheHouse();
                    }
                }, function(error) {
                    // promise rejected, could log the error with: console.log('error', error);
                    sweepTheHouse();
                });
        };

【问题讨论】:

  • 只要它在相同的上下文中,并且您可以引用两个控制器中可用的承诺,这应该不是什么大问题,但是这个问题似乎很模糊要负责吗?
  • 是的,只需创建多个使用来自服务的相同承诺的控制器。它可以开箱即用。
  • @Bergi,你能看看我的编辑吗?您是说我可以制作母控制器,而 sun 服务不会为此进行额外的 http 调用吗?基本上一个http请求,每个控制器都可以在解决后使用它?我关心的原因是因为请求正在执行大型查询。
  • 不,他们不会 - (共享)服务不会每次都返回相同的承诺,每当您调用 getWeather 时都会创建一个新的承诺(和请求)。 You can easily cache it 虽然。
  • 在 http 请求上使用“cache: true”怎么样。这不是解决了多个请求的问题,而不必摆弄将 Promise 暴露给多个控制器吗?

标签: javascript angularjs q angular-promise angular-resource


【解决方案1】:

要让您的工厂服务只获取一次 url,请将 httpPromise 存储在您的工厂服务中。

app.factory('SonService', function ($http) {
    var weatherPromise;
    function getWeather() {
      return $http.get('http://fishing-weather-api.com/sunday/afternoon')
                .then(function(response) {
                    if (typeof response.data === 'object') {
                        return response.data;
                    } else {
                        // invalid response
                        throw response;
                    }

                }, function(response) {
                    // something went wrong
                    throw response;
                });
    }
    function sonService() {
      if (!weatherPromise) {
        //save the httpPromise
        weatherPromise = getWeather();
      }
      return weatherPromise;
    }
    return sonService;
});

【讨论】:

    【解决方案2】:

    简单的答案,以非特定角度(但很容易应用于 Angular)的方式,是创建一个缓存 ON-OUTBOUND-REQUEST 的服务(而不是像大多数系统那样缓存返回值)。

    function SearchService (fetch) {
      var cache = { };
      return {
        getSpecificThing: function (uri) {
          var cachedSearch = cache[uri];
          if (!cachedSearch) {
            cachedSearch = fetch(uri).then(prepareData);
            cache[uri] = cachedSearch;
          }
          return cachedSearch;
        }
      };
    }
    
    
    function A (searchService) {
       var a = this;
       Object.assign(a, {
         load: function ( ) {
           searchService.getSpecificThing("/abc").then(a.init.bind(a));
         },
         init: function (data) { /* ... */ }
       });
    }
    
    function B (searchService) {
      var b = this;
      Object.assign(b, {
        load: function ( ) {
          searchService.getSpecificThing("/abc").then(b.init.bind(b));
        },
        init: function (data) { /* ... */ }
      });
    }
    
    
    var searchService = SearchService(fetch);
    var a = new A(searchService);
    var b = new B(searchService);
    
    a.load().then(/* is initialized */);
    b.load().then(/* is initialized */);
    

    他们共享相同的承诺,因为他们正在与之交谈的服务缓存并返回相同的承诺。

    如果您想安全起见,您可以缓存一个 Promise,然后返回基于缓存的 Promise 解析(或拒绝)的新 Promise 实例。

    // instead of
    return cachedSearch;
    
    // replace it with
    return Promise.resolve(cachedSearch);
    

    现在,每次您发出请求时,每个用户都会获得一个新实例,但每个实例也会根据原始缓存调用通过或失败。
    当然,您可以更进一步,对缓存设置时间限制,或者使用挂钩来使缓存无效,或者其他任何...

    将其转换为 Angular 也很容易

    • SearchService 是一项服务
    • AB 是控制器
    • 使用 $http 而不是 fetch(虽然 fetch 真的很漂亮)
    • fetch( ).then(prepareData) 中,您将在成功时从 JSON 转换数据;
      $http 中,您将返回response.data,因为您的用户不想这样做
      无论哪种方式,每次出站呼叫都只执行一次该操作,因此也要缓存它
    • 使用$q(和q方法)代替原生Promise
    • 使用angular.extend,而不是Object.assign
    • 你已经完成了;您现在已经将整个概念移植到 Angular 和 VanillaJS 中

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多