-
Notifications
You must be signed in to change notification settings - Fork 27.5k
feat($resource): add support for cancelling requests #13210
Conversation
This is an alternative approach to #13058 as discussed in #13058 (comment). Pending:
/cc @petebacondarwin |
The API looks good to me. I haven't looked at the implementation but I think it is worth pursuing this line. |
Good job! +1 |
|
@gkalpak if you can get this up and running before next release it can go in 1.5.0 otherwise let's punt it to 1.6 |
@petebacondarwin , the problem is that the user should then always check if the method is available before calling it (and that makes the API weird). Consider the typical usecase, where the app loads some data based on a user's choice. As soon as the user changes their choice, the old request is irrelevant and a new one should be made. At this point, it is a good practice to cancel the previous request, but we don't really know if that request has already completed or is still pending. |
OK, an alternate API is that you pass the object back to a cancel request on the original resource object... |
Would be possible, but requires more "housekeeping", right ? What are the benefits of calling a static method (maybe I missed something) ? |
The benefit is that you don't need to modify the properties on the instance object. var postsResource = $resource(...);
var posts = postsResource.query(...);
postsResource.cancelRequest(posts); |
Yes, but you need to keep an association between requested resources/instances and promises internally, so I feel there is more housekeeping to be done. It would be also more easy to introduce memory leaks (if at all avoidable without WeakMaps). So, off the top of my head, implementation-wise, modifying the instance properties should be preferrable/simpler. It is also more consistent imo with But from a user's perspective, would there be any benefit ? |
Do the |
If we provide a new promise in the |
That would work as well, but:
|
The |
We found a Contributor License Agreement for you (the sender of this pull request) and all commit authors, but as best as we can tell these commits were authored by someone else. If that's the case, please add them to this pull request and have them confirm that they're okay with these commits being contributed to Google. If we're mistaken and you did author these commits, just reply here to confirm. |
7967347
to
392a261
Compare
CLAs look good, thanks! |
I added the docs (and the BC notice can be added upon squashing). Will try to get a look at it today. |
So, it seems this somehow related to how the dgeni-packages/links#getDocFromAlias service works In any case, the issue in this PR can be solved by changing |
392a261
to
ea65cfb
Compare
Introduced changes: - Deprecate passing a promise as `timeout` (for `$resource` actions). It never worked correctly anyway. Now a warning is logged (using `$log.debug()`) and the property is removed. - Add support for a boolean `cancellable` property in actions' configuration, the `$resource` factory's `options` parameter and the `$resourceProvider`'s `defaults` property. If true, the `$cancelRequest` method (added to all returned values for non-instance calls) will abort the request (if it's not already completed or aborted). If there is a numeric `timeout` specified on the action's configuration, the value of `cancellable` will be ignored. Example usage: ```js var Post = $resource('/posts/:id', {id: '@id'}, { get: { method: 'GET', cancellable: true } }); var currentPost = Post.get({id: 1}); ... // A moment later the user selects another post, so // we don't need the previous request any more currentPost.$cancelRequest(); currentPost = Post.get({id: 2}); ... ``` BREAKING CHANGE: Using a promise as `timeout` is no longer supported and will log a warning. It never worked the way it was supposed to anyway. Before: ```js var deferred = $q.defer(); var User = $resource('/api/user/:id', {id: '@id'}, { get: {method: 'GET', timeout: deferred.promise} }); var user = User.get({id: 1}); // sends a request deferred.resolve(); // aborts the request // Now, we need to re-define `User` passing a new promise as `timeout` // or else all subsequent requests from `someAction` will be aborted User = $resource(...); user = User.get({id: 2}); ``` After: ```js var User = $resource('/api/user/:id', {id: '@id'}, { get: {method: 'GET', cancellable: true} }); var user = User.get({id: 1}); // sends a request instance.$cancelRequest(); // aborts the request user = User.get({id: 2}); ``` Fixes angular#9332 Closes angular#13050 Closes angular#13058
ea65cfb
to
e15961e
Compare
@gkalpak thanks for the investigation but that is indeed how dgeni-packages is supposed to work :-) |
I fixed the @petebacondarwin, isn't it kind of unexpected that if there is only 1 "doc definition" then it is returned (even if doesn't match the |
* `transformResponse` to an empty array: `transformResponse: []` | ||
* By default, transformResponse will contain one function that checks if the response looks | ||
* like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, | ||
* set `transformResponse` to an empty array: `transformResponse: []` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and above, I just truncated the lines to 100 chars max.
Sorry, I couldn't help myself 😃
The way it works is that we match the text you provide against all the aliases of the docs in the system.
|
I see. This makes sense as well, I guess. Tweaking the "smartness" vs predictability ratio is a matter of preferrence after all 😃 |
I guess we could also have a heuristic to favour |
It doesn't ! Now that I now the reasoning behind it, it makes sense, so it's fine by me :) |
* will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's | ||
* return value. Calling `$cancelRequest()` for a non-cancellable or an already | ||
* completed/cancelled request will have no effect.<br /> | ||
* **Note:** If a timeout is specified in millisecondes, `cancellable` is ignored. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is that so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By design 😄
Basically, both need to pass a value as timeout to $http
's config (either the number of milliseconds or a promise).
You can't use both a numeric timeout and a promise, so I chose for timeout
to take precedence.
(Note that calcellable
can be configured as a global default or as per resource default (using the options
argument), while timeout
can't.)
if (!isInstanceCall && action.cancellable) { | ||
var deferred = $q.defer(); | ||
httpConfig.timeout = deferred.promise; | ||
value.$cancelRequest = deferred.resolve; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to worry about memory leaks if this promise gets attached to the http request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and the returned value
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't given it too much thought tbh, but here is what I think:
httpConfig
andvalue
do not reference each other, so the question is whether each one is disposed of properly on its own.httpConfig
should get gc'ed (but maybe it doesn't because it is referenced inside the promise callback - I am not sure). If it doesn't, we should fix it. In any case, this is not introduced by or in any way related to changes in this PR.value.$cancelRequest
does live on, so we need to de-referencedeferred
as soon as that's possible, in order to enable it to be gc'ed. That is what's done in thefinally
block.
I might have missed something though, so take the above with a grain of salt.
I added a commit to replace calls to |
Note to self: This needs to be backported to |
Hmm - I feel uncomfortable about backporting to 1.4.8. I think it might be safer to just revert 7170f9d instead? |
Works for me as well. |
And maybe also log a warning on |
@petebacondarwin, thx for merging ! So, what about
|
Introduced changes:
timeout
(for$resource
actions).It never worked correctly anyway.
Now a warning is logged (using
$log.debug()
) and the property isremoved.
cancellable
property to actions'configuration, the
$resource
classesoptions
of the$resourceProvider
's defaults.If true, the
$cancelRequest
method (added to all returned values fornon-instance calls) will abort the request (if it's not already
completed or aborted).
If there is
timeout
specified on the action's configuration, the valueof
cancellable
is ignored.Example usage:
Fixes #9332
Closes #13050
Closes #13058