用于确保单一主动承诺的角度模式

时间:2016-01-27 05:52:47

标签: angularjs ajax design-patterns

在我们的应用程序中,我们有一个搜索输入字段。通常,在用户输入(Google即时消息)时会发送请求,并显示结果。

显然,可能会发生以下情况:

  1. 用户类型,导致ajaxRequest1
  2. 用户继续输入,产生ajaxRequest2
  3. 接收并显示
  4. 对应于ajaxRequest2的results2
  5. 此后,接收对应于ajaxRequest1的results1。显然,由于ajaxRequest2是在ajaxRequest1之后发送的,我们只关心results2而不是results1。
  6. 编辑:这里显而易见的答案是"使用去抖"。出于保密和简洁的原因,我在这里只是说它在我们的特定场景中不会起作用。我知道什么是去抖动,我已经考虑过了。

    在伪代码中,我们曾经像这样处理它:

    $scope.onInput = function() {
      var inputText = getInput();
      SearchService.search(inputText).then(function(results) {
        // only display if input hasn't changed since request was sent
        if(inputText === getInput()) {
          displayResults(results);
        }
      });
    };
    

    由于这涉及很多样板并且看起来很丑陋,我们采用了SearchService管理事情的模式

    $scope.onInput = function() {
      var inputText = getInput();
      SearchService.search(inputText).then(function(results) {
        displayResults(results);
      });
    }
    
    function SearchService() {
      var cachedSearchDeferred;
      this.search = function(inputText) {
        if(cachedSearchDeferred) {
          //means there's an unresolved promise corresponding to an older request
          cachedSearchDeferred.reject();
        }
    
        var deferred = $q.deferred();
        $http.post(...).then(function(response) {
          // saves us having to check the deferred's state outside
          cachedSearchDeferred = null;
          deferred.resolve(response.data);
        });
        cachedSearchDeferred = deferred;
        return deferred.promise;
      }
    }
    

    这很好用。 SearchService创建一个延迟,其中包含与最近一次调用SearchService.search相对应的承诺。如果对SearchService.search进行了另一次呼叫,则拒绝旧的延期,并根据新呼叫创建新的延期。

    两个问题:

    1. 这是一个很好的模式来做我们需要的 - 主要是请求锁定?我们希望确保只有最新请求的承诺才能成功解决
    2. 如果我们有其他需要表现相似的SearchService方法,那么这个延迟的样板需要在每个方法中。还有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

@Jayraj取决于你想要制作http api的复杂程度。你可以走得很深,但如果我理解你的问题,你正在寻找一个http超时拦截器。使用Angular $ httpProvider,您可以注册一个需要返回响应和请求的自定义拦截器。

我应该注意到我已经从不同的代码库中对此进行了法兰克借,所以我不会对代码表示赞赏,但它是清晨,需要在我的库中找到源代码,但是要定向帮助最佳实践在这里。

ANGULAR.JS示例

  1. 有角度的团队给出了这个例子

    $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
      return {
       'request': function(config) {
           // same as above
        },
    
        'response': function(response) {
           // same as above
        }
      };
    });
    
  2. 创建一个工厂对象,其中包含http端点配置,即配置文件,其中包含服务器组件和端点,该端点标识了端点的UID,即它去向何处以及发送者是谁

    (function() {
        'use strict';
        var config = {
            server: {
                url: null
            },
            endpoint: {
                url: null,
                uuid: null,
            }
        };
    
        return angular.module('matrixme.config', [
    
        ]).constant('config', config);
    })();
    
  3. 为了简洁起见,我将省略服务提供商代码,但您需要构建一个REST api服务提供程序,然后将其注入所有相关类。提供商将有效配置您的配置对象,例如用户,文章,并将作为api电话的家。

  4. 您可以创建自己的拦截器并注入:

     (function() {
      'use strict';
    
      angular.module('matrixme.api', ['matrixme.config'])
        .config(['$httpProvider', function($httpProvider) {
            $httpProvider.interceptors.push('timeoutInterceptor');
        }]);
      })();
    
  5. 在注入之前构建注射器:)我没有测试过这个但是真的回答了你的最佳实践问题。所以这是方向性的,但您可以创建您的请求和响应。您可以构建多个自定义拦截器,例如uuid,auth timeout等。

     (function() {
      'use strict';
    
    TimeoutInterceptor.$inject = ['$timeout', '$q', '$rootScope', 'request'];
    function TimeoutInterceptor($timeout, $q, $rootScope, request) {
    
    return {
    
        request: function(config) {
            if ((config.url)) {
                config._ttl = config._ttl ? Math.min(2000, config._ttl * 2) : 2000;
                config.timeout = $timeout(function() {
                    config._isTimeout = true;
                }, config._ttl);
            }
    
            return config;
        },
    
        response: function(response) {
            if (response.config.timeout) {
                $timeout.cancel(response.config.timeout);
                $rootScope.serverStatus = 0;
            }
    
            return response;
        },
    };
    }
    
    angular.module('matrixme.api')
     .factory('timeoutInterceptor', TimeoutInterceptor);
    
     })();
    

答案 1 :(得分:0)

事实证明,已经存在一个解决方案:RxJS。其自述文件中的example几乎就是这种情况。

array=("list1item1 list1item2" "list2item list2item2")

for list in "${array[@]}"
do
    for item in $list
    do
        echo $item
    done
done

还有RxJS for Angularconst $input = $('#input'); /* Only get the value from each key up */ var keyups = Rx.Observable.fromEvent($input, 'keyup') .pluck('target', 'value') .filter(text => text.length > 2 ); /* Now debounce the input for 500ms */ var debounced = keyups .debounce(500 /* ms */); /* Now get only distinct values, so we eliminate the arrows and other control characters */ var distinct = debounced .distinctUntilChanged(); /* Once that is created, we can tie together the distinct throttled input and query the service. In this case, we'll call flatMapLatest to get the value and ensure we're not introducing any out of order sequence calls. */ const suggestions = distinct .flatMapLatest(() => { // Do XHR and return a promise // flatMapLatest will always use the latest one }); 对象添加内容。