首页 文章

AngularJS:使用前/后解析/拒绝操作来增强或包装承诺

提问于
浏览
5

Goal

我正在尝试创建一系列承诺“增强器”,它们将围绕现有的承诺(简单的http请求)添加功能(如缓存,排队,重定向处理等) .

Problem

问题是我已经将一个已经增强的承诺包装成了一个非常有用的请求,当我通过返回一个新的 $q 将它包装在一个新的承诺中时,这些问题就会丢失 .

Question

我可以使用什么模式来增强或包装承诺(如下面的两个示例中所示),但不会丢失承诺可能具有的任何其他(非冲突)增强功能?

Example 1

这是一个自动处理503-Retry-After错误的示例:

function _enhancePromiseWithAutoRetry(promise) {
  var enhancedPromise = $q(function(resolve, reject) {
    var newReject = get503Handler(this, resolve, reject);
    promise.then(resolve, newReject);
  });

  // 503 handling isn't enabled until the user calls this function.
  enhancedPromise.withAutoRetry = function(onRetry, timeout) {
    var newPromise = angular.copy(this);
    newPromise._503handled = true;
    newPromise._503onRetry = onRetry;
    newPromise._503timeout = timeout;
    return newPromise;
  };

  return enhancedPromise;
}

我的想法是,如果我返回使用上述功能增强的承诺,用户可以:

someRequest.withAutoRetry().then(onSuccess, onError);

或者更清楚(带链接):

someRequest.then(onSuccess, onAnyError)
           .withAutoRetry().then(onSuccess, onNon503Error);

在这里,如果服务器忙,第一次调用 then(...) 可能会立即出错,但是 .withAutoRetry() 之后的调用将在重复请求之前轮询服务器,直到响应成功,或者返回非 RetryAfter 错误 .

Example 2

这是另一个添加自定义缓存行为的示例:

function _enhancePromiseWithCache(promise, cacheGet, cachePut) {
  // Wrap the old promise with a new one that will get called first.
  return $q(function(resolve, reject) {
    // Check if the value is cached using the provided function
    var cachedResponse = cacheGet !== undefined ? cacheGet() : undefined;
    if(cachedResponse !== undefined){
      resolve(cachedResponse);
    } else {
      // Evaluate the wrapped promise, cache the result, then return it.
      promise.then(cachePut);
      promise.then(resolve, reject);
    }
  });
}

这个允许库设置数据缓存,可以使用它而不是向服务器发出请求,并且可以在请求完成后添加 . 例如:

lib.getNameOrigin = function(args) {
  var restRequest = Restangular.all('people').one(args.id).get('nameOrigin');
  // Cache, since all people with the same name will have the same name origin
  var enhancedPromise = _enhancePromiseWithCache(restRequest,
                          function(){ return nameOrigins[args.name]; },
                          function(val){ nameOrigins[args.name] = val; });
  return enhancedPromise;
}

别处

// Will transparently populate the cache
lib.getNameOrigin({id: 123, name:'john'}).then(onSuccess, onError).then(...);

而在其他地方完全

// Will transparently retrieve the result from the cache rather than make request
lib.getNameOrigin({id: 928, name:'john'}).then(onSuccess, onError);

Possible Solution

我've considered copying the original promise, but then overwriting the new one' s then 函数的实现引用了原始的promise then (使用Proxy Pattern),但这样安全吗?我知道承诺不仅仅是 then 功能 .

2 回答

  • 3

    这是我在 Possible Solution 部分中提出的解决方案,因此可以对其进行详细讨论 .

    我've considered copying the original promise, but then overwriting the new one' s then 函数有一个解决原始承诺的实现,但这样安全吗?

    New Example

    function _enhancePromiseWithQueuing(promise, id) {
      // Copy the old promise and overwrite its then method.
      var enhancedPromise = angular.copy(promise);
      enhancedPromise.then = function(resolve, reject) {
        // Resolves the original promise once the existing `id` queue is clear.
        queue.enqueueRequest(id, function() { promise.then(resolve, reject); });
        return this;
      };
      return enhancedPromise;
    }
    

    Example 1 (来自上方)

    function _enhancePromiseWithAutoRetry(promise) {    
      // Copy the old promise and enhance it with the withAutoRetry method.
      var enhancedPromise = angular.copy(promise);
      // Add a function that enables 503 Retry-After handling when called.
      enhancedPromise.withAutoRetry = function(onRetry, timeout) {
        // Copy the old promise and overwrite its then method.
        var promiseWith503Handling = angular.copy(this);
        promiseWith503Handling.then = function(resolve, reject) {
          // Call the original promise then method with a modified reject handler.
          return this.then(resolve, get503Handler(this, resolve, reject,
                                                  onRetry, timeout, new Date()));
        };
        return promiseWith503Handling;
      };    
      return enhancedPromise;
    }
    

    Example 2 (来自上方)

    function _enhancePromiseWithCache(promise, cacheGet, cachePut) {
      var enhancedPromise = angular.copy(promise);
      enhancedPromise.then = function(resolve, reject) {
        // Check if the value is cached using the provided function
        var cachedResponse = cacheGet !== undefined ? cacheGet() : undefined;
        if(cachedResponse !== undefined){
          return resolve(cachedResponse);
        } else {
          // Resolve the original promise, cache the result, then return it.
          promise.then(cachePut);
          return promise.then(resolve, reject);
        }
      };
      return enhancedPromise;
    }
    
  • 0

    解决方案不是为了增强承诺本身,而是为了创造它们的工厂 .

    使用函数式编程和/或面向方面的编程方法来装饰原始函数 . 这不仅会减少错误,而且更简洁,可组合和可重用 .

    function decorate(makeThenable) {
        return function(...args) {
            … // before creating the thenable
            return makeThenable(...args).then(function(value) {
                … // handle fulfillment
                return …; // the resulting value
            }, function(error) {
                … // handle rejection
                return …; // (or throw)
            });
        };
    }
    var decorated = decorate(myThenablemaker);
    decorated(…).then(whenFulfilled, whenRejected);
    

    Example 1:

    function withAutoRetry(request, timeout) {
        return function() {
            var args = arguments;
            return request.apply(null, args).catch(function handle(e) {
                if (e instanceof Http503Error) // or whatever
                    return request.apply(null, args).catch(handle);
                else
                    throw e;
            });
        };
    }
    
    withAutoRetry(someRequest)().then(onSuccess, onError);
    
    withAutoRetry(function() {
        return someRequest().then(onSuccess, onAnyError);
    })().then(onSuccess, onNon503Error);
    

    Example 2:

    function withCache(request, hash) {
        var cache = {};
        if (!hash) hash = String;
        return function() {
            var key = hash.apply(this, arguments);
            if (key in cache)
                return cache[key];
            else
                return cache[key] = request.apply(this, arguments);
        };
    }
    
    lib.getNameOrigin = withCache(function(args) {
        return Restangular.all('people').one(args.id).get('nameOrigin');
    }, function(args) {
        return args.name;
    });
    

相关问题