AngularJS: Strengthening or wrapping promises with pre / post elimination / reject actions

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); }); // 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; } 

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'); // 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; } 

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.

+5
source share
2 answers

The solution is not to improve the promises themselves, but the factories that create them.

Use functional programming and / or aspect-oriented programming approaches to enhance the original function. It will be not only erroneous, but also more concise, compound and reusable.

 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; }); 
+3
source

Here is the solution that I proposed in the Possible Solution section so that it can be discussed in detail.

I considered copying the original promise, but then overwriting the new then function with an implementation that resolves the original promise, but is it safe?

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 (top)

 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 (top)

 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
source

Source: https://habr.com/ru/post/1200844/


All Articles