在多个控制器中看到的承诺解决方案

promise resolution seen in multiple controllers

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

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

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

这是我从这篇文章中借用的一些代码(我会添加一个 'mother controller' 来说明我的示例,我不希望子服务进行两次 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();
                });
        };

要让您的工厂服务仅获取 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;
});

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

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 */);

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

如果你想安全起见,你可以缓存一个承诺,然后 return 新的承诺实例根据缓存的承诺解析(或拒绝)。

// instead of
return cachedSearch;

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

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

将其转换为 Angular 也很简单

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