目标
我正在尝试创建一系列承诺'增强器',它将围绕现有的简单http请求的承诺添加功能(如缓存,排队,重定向处理等)。
问题
我正在使用这种增强承诺的方法遇到的问题是,如果增强功能将任何功能或公共可访问的属性添加到承诺中(或者如果我将已经增强的承诺包含在一个非常有效的请求中),那么这些是当我通过返回新的$q
将其包装在新的承诺中时丢失了。
问题
我可以使用什么模式来增强或包装承诺(如下面的两个示例中所示),但不会丢失承诺可能具有的任何其他(非冲突)增强功能?
示例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
错误。
示例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);
可能的解决方案
我考虑过复制原始的promise,但是后来用一个引用原始promise then
的实现(使用Proxy Pattern)覆盖新的then
函数,但这是安全?我知道承诺不仅仅是then
函数。
答案 0 :(得分:3)
解决方案不是为了增强承诺本身,而是为了创造它们的工厂。
使用函数式编程和/或面向方面的编程方法来修饰原始函数。这不仅会减少错误,而且更简洁,可组合和可重复使用。
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);
示例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);
示例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;
});
答案 1 :(得分:0)
以下是我在可能的解决方案部分中提出的解决方案,以便对其进行详细讨论。
我考虑过复制原始的承诺,但是用一个解决原始承诺的实现来覆盖新版本的then
函数,但是这样安全吗?
新示例
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;
}
示例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;
}
示例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;
}