-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
abort pending requests in Backbone.sync #1325
Conversation
One concern is people who have overridden Backbone.ajax need to return something that behaves like a deferred object, but usually people just define a new Backbone.sync function, so I think impact will be minimal, but I could be wrong. If they return nothing, the code will not break. If they return something, it needs to act like a deferred object. |
From the docs:
Since While I understand the usefulness of this approach (I've implemented something similar several times), I think |
Plugin of this https://github.com/amccloud/backbone-safesync |
@braddunbar
When would you ever want the collection to reset to anything other than the last call to fetch? More importantly, when would you ever want |
I just ran into this problem while implementing live search, where some partial word queries that weren't cached took longer than complete words ("snowboa" vs "snowboard"). It would be great if this was the the default behavior with an option such as |
This is definitely a useful behavior, but I think it belongs in a plugin that wraps |
So far nobody has said why it belongs in a plugin. Again I ask:
Apps with the current Backbone.sync are naive w/o this behavior. |
You're right, I didn't explain myself well. Sorry about that. :)
There are two reasons I think.
|
@braddunbar Speaking to your second point, and perhaps both points, that's why a |
@pwightman As for point one, I think requests come back in reverse order more than you might think. With multiple backend-endpoints for a system you can never be sure how long a request will take. That said, point number two is what really worries me here. It makes otherwise successful requests appear to have failed and renders the result of the returned promise useless since I can't tell if it failed due to a subsequent request or an actual failure. For instance, the following code snippet will display an error message for a request that was successful, but resent before completion. I don't think this is the behavior we're looking for. model.save(...)
.done(function(){
// handle response
})
.fail(function() {
// display error message
}); |
model.save(...)
.done(function(){
// handle response
})
.fail(function(xhr, status) {
if (status === 'abort') return;
// display error message
}); Or do the abort check elsewhere before it gets here (in jQuery). |
I'm not sure what you mean here. Are you saying I should patch jQuery? |
@braddunbar You could surely check the textStatus and errorThrown passed to Edit: @rpflorence beat me to it. |
Also, there is no way to tell why the request was aborted. Was it aborted due to a resent request or some other legitimate reason for which an error should be displayed? |
http://api.jquery.com/ajaxError/ is an option (not requiring patching), but I personally just do the check in the fail handler. |
The only way its aborted is if this version of Backbone.sync aborts it or you do it yourself somewhere, in which case you display the error message then. There might be an argument here to do something to say it was aborted by backbone.sync, maybe trigger an event on the collection / model. |
when collection.fetch is called multiple times (perhaps due to user-interaction with the page) an earlier request could land _after_ a later request, causing unexpected state in the app this commit tracks the last requests across all collections and models and their sync methods, checks if its pending, and aborts it. can be overridden with `{abortPending: false}` in the sync options object
Summarizing a conversation in IRC, we can abort the request and send a textStatus to the jqXHR deferred of 'stale', so you can do different error handling. These changes are incorporated in the pull request now. collection.fetch().fail(function(jqXHR, status) {
if (status === 'stale') {
// do something
}
}); And renamed the option to collection.fetch({abortPending: false}); |
There are actually three different cases that I'm aware of -- and your app can have mixes of all three, even within a single "interaction":
Part of the problem is that this logic can span different models and different collections. If I delete a collection, and then modify a model that was in it, that's inherently a broken action. If I wait for the collection delete to succeed, then my queued request is broken. I don't think that this is something that's wise for Backbone to try to do for you by default -- especially when many of the interesting interactions in this space are across different types of models that the system doesn't necessarily know are logically related. We want to give you the primitives to sequence these things yourself. |
I was having similar issue, where I had requests that returned at different times, and sometimes the success would render in the wrong order. I created what I call LastCall, it is a function factory/decorator. https://github.com/pituki/LastCall |
Sending a pull request to start a conversation, happy to make adjustments to my branch if the team feels like this is a valid patch. @amccloud and I collaborated a bit on this. He's got a plugin for the same purpose here.
Purpose
It's not uncommon to have user interaction trigger a collection.fetch or model.fetch. Imagine a user clicking several filters for a list of items, or a search field fetching results as the user types. Usually the requests sent all come back in the right order--but not always.
Consider this:
The solution is to cancel the pending request.
Commit Message
when collection.fetch is called multiple times
(perhaps due to user-interaction with the page)
an earlier request could land after a later
request, causing unexpected state in the app
this commit tracks the last requests across all
collections and models and their sync methods,
checks if its pending, and aborts it.
can be overridden with
{safe: false}
in the syncoptions object