goal
I am trying to create a series of promising "enhancers" that will add functionality (such as caching, queuing, forwarding, etc.) around existing promises, which are simple HTTP requests.
Problem
The problem I am encountering with this method of extending promises is that if the extension adds any features or public properties to the promise (or if I wrap an already extended promise like a restatial query) they are lost when I wrap it with a new one promise, returning a new $q .
Question
Which template can I use to improve or port promises (as in the two examples below), but without losing any other (without conflicts) improvements, promises can have?
Example 1
Here is an example that automatically handles 503-Retry-After errors:
function _enhancePromiseWithAutoRetry(promise) { var enhancedPromise = $q(function(resolve, reject) { var newReject = get503Handler(this, resolve, reject); promise.then(resolve, newReject); });
The idea is that if I return a promise reinforced with the above function, the user can go:
someRequest.withAutoRetry().then(onSuccess, onError);
Or to be more clear (with a chain):
someRequest.then(onSuccess, onAnyError) .withAutoRetry().then(onSuccess, onNon503Error);
Here, the first call to then(...) can be immediately deleted if the server is busy, but calls after .withAutoRetry() will interrogate the server with repeated requests until the response is successful or the RetryAfter error is returned.
Example 2
Here is another example that adds custom caching behavior:
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); } }); }
This allows the library to configure a data cache that can be used instead of server requests, and can be added after the request is complete. For instance:
lib.getNameOrigin = function(args) { var restRequest = Restangular.all('people').one(args.id).get('nameOrigin');
In the other place
// Will transparently populate the cache lib.getNameOrigin({id: 123, name:'john'}).then(onSuccess, onError).then(...);
And somewhere else completely
// Will transparently retrieve the result from the cache rather than make request lib.getNameOrigin({id: 928, name:'john'}).then(onSuccess, onError);
Possible Solution
I considered copying the original promise, but then overwriting the new then function with an implementation that references the original then promise (using the Proxy template ), but is it safe? I know there are a lot more promises than just the then function.