Javascript AngularJS:通过前/后解决/拒绝行动增强或包装承诺
目标 我正在尝试创建一系列承诺“增强器”,它们将围绕简单http请求的现有承诺添加功能(如缓存、排队、重定向处理等) 问题 我在使用这种增强承诺的方法时遇到的问题是,如果一个增强向承诺添加了任何函数或可公开访问的属性(或者如果我将一个已经增强的承诺包装为重新启动的请求),那么当我通过返回一个新的Javascript AngularJS:通过前/后解决/拒绝行动增强或包装承诺,javascript,angularjs,promise,restangular,angular-promise,Javascript,Angularjs,Promise,Restangular,Angular Promise,目标 我正在尝试创建一系列承诺“增强器”,它们将围绕简单http请求的现有承诺添加功能(如缓存、排队、重定向处理等) 问题 我在使用这种增强承诺的方法时遇到的问题是,如果一个增强向承诺添加了任何函数或可公开访问的属性(或者如果我将一个已经增强的承诺包装为重新启动的请求),那么当我通过返回一个新的$q将其包装为一个新的承诺时,这些函数或属性就会丢失 问题 我可以使用什么模式来增强或包装承诺(如下面的两个示例),但不会丢失承诺可能具有的任何其他(非冲突的)增强 示例1 下面是一个将自动处理503错误
$q
将其包装为一个新的承诺时,这些函数或属性就会丢失
问题
我可以使用什么模式来增强或包装承诺(如下面的两个示例),但不会丢失承诺可能具有的任何其他(非冲突的)增强
示例1
下面是一个将自动处理503错误后重试的示例:
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);
或者更清楚地说(使用链接):
在这里,如果服务器忙,对然后(…)
的第一次调用可能会立即出错,但是之后的调用将使用重复请求轮询服务器,直到响应成功,或者返回非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);
可能的解决方案
我曾考虑复制原始承诺,但随后使用引用原始承诺的then
(使用)的实现覆盖新承诺的then
函数,但这安全吗?我知道承诺远不止是then
函数。这是我在可能的解决方案一节中提出的解决方案,可以详细讨论
我曾考虑复制原始承诺,但随后使用解决原始承诺的实现覆盖新承诺的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(自上而下)
示例2(自上而下)
解决办法不是强化承诺本身,而是强化创造承诺的工厂
使用函数式编程和/或面向方面的编程方法来修饰原始函数。这不仅不容易出错,而且更加简洁、可组合和可重用
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;
});
};
}
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);
};
}
示例2:
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;
});
};
}
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);
};
}
这在角度1.2和角度1.3之间变化,这与您相关吗?你希望我关注哪一个?在这两种情况下,您都必须覆盖然后
,只要您的,它就是安全的。然后
是Promises/A+complaintAlso,我不确定我是否理解您的用例$http(…).cache()。然后(…
有意义,但是$http(…)。然后(function(){return 3})。cache()
没有意义,3被缓存了吗?在哪里?到了什么程度?这些方法只会影响另一面,这实际上是有意义的。你能更具体地说明你在添加什么吗?@BenjaminGruenbaum angular的最新版本很好。.catch()呢
方法?是否也必须重写该方法,还是保证只调用。然后(未定义,拒绝)
?保证?不,但实际上确实如此。它只保证具有相同的效果。尽管-我认为我所写的关于链接问题的文章从一开始就很重要。链接后缓存或排队意味着什么?@BenjaminGruenbaum第二个用例是库返回enhancePromiseWithCache($http(…),cache.makeGetterFor(args),cache.makePutterFor(args))
。用户通常会返回consume承诺:someRequest.then(onSuccess,onFailure)。then(somethingElse);
,只有$http
请求/响应被拦截。覆盖then
可能是安全的,但需要大量注意(容易出错),可能会破坏promise library提供的大多数优化;以及其他一些依赖于库的功能。@Bergi我认为这会很危险,但我找不到任何可能导致问题的示例。如果没有错误,它可能会破坏哪些功能或优化?我可以在您的I实现是新的then
不返回任何内容。诸如进度消息或取消(无论promise库支持什么)等功能需要集成到解决方案中,并使其非常依赖于实现(与我的方法不同,在交换promise库时不需要修改)。优化也依赖于实现,许多库使用比相关承诺的then
方法更小的原语来实现它们的then
方法。没错,在从$q
构造函数切换后,我错过了添加返回语句。我发现很难将您的原语与特定功能的校正(缓存/503处理)从您的一般方法来看。据我所知,您的方法主要与使用apply
有关。您是否可以详细说明或添加一个示例,其中包括使用发生的任意操作包装任意启用:1.在resolve
之前,2.在resolve
之后,3.在之前拒绝>,4.在reject
之后?从示例2中,它看起来像是调用lib.getNameOrigin({id:123,name:'john'}
withAutoRetry(someRequest)().then(onSuccess, onError);
withAutoRetry(function() {
return someRequest().then(onSuccess, onAnyError);
})().then(onSuccess, onNon503Error);
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;
});