1

I have a main view that has a box that has subviews that you can switch between

After a fetch ajax call is successful, a 'success' div on the main view is shown.

The problem I'm facing is that, when I switch between subviews, the deferred promise from the previous view's fetch still gets executed and shows the 'success' div.

this is my code:

this.model.fetch().done(function() {
  $('#success').show();
});

onChangeSubview: function() {
    this.subview.unbind();
    this.subview.remove();
}

This SO question abort-ajax-requests-using-jquery says you can store the pointer to the deferred and stop it by calling abort() like so:

var fetchXhr = this.model.fetch().done(function() {
  $('#success').show();
});
onChangeSubview: function() {
    fetchXhr.abort();
}

I have several fetches for each subview and some are repeated in an interval, that would mean I would have to store every fetch request and loop through them calling abort on each.

Is there a better way to stop the deferred function from being executed? eg: some kind of check to see if the subview has been removed, or a function to unbind all deferred promises?

Community
  • 1
  • 1
Ta946
  • 1,342
  • 12
  • 19
  • 1
    Just thinking out loud, could you add all your fetches for a subview to a list and then onChangeSubview you could iterate over them and execute the abort e.g. `this.fetches.forEach(function(fetchXhr) { fetchXhr.abort(); });` (or `_.invoke(this.fetches, 'abort');` if you want to do it the Underscore way). You could potentially even put this functionality in a base view that you extend so you could abstract some of it away. – JVDL Nov 11 '16 at 10:19
  • 1
    @Dymos +1. You are being too modest, that's not a comment but an actual answer ;) – hashchange Nov 11 '16 at 10:38
  • haha thanks :-) I'll add it as an answer for posterity then. – JVDL Nov 11 '16 at 10:40

2 Answers2

1

One way to solve this would be to add all your fetches for a subview to a list and then onChangeSubview iterate over them and execute the abort e.g.

this.fetches.forEach(function(fetchXhr) { 
    fetchXhr.abort(); 
}); 

or if you want to do it the Underscore way

_.invoke(this.fetches, 'abort');

You could potentially even put this functionality in a base view that you extend so you could abstract some of it away.

JVDL
  • 1,157
  • 9
  • 16
1

Aborting isn't necessarily desirable as you may want views to continue to be refreshed even when hidden (acknowledging that you currently unbind/remove).

You can certainly avoid the need to abort by adopting a pattern that causes the '#success' element to be shown conditionally, eg by rigging a $('#success') object with a method that returns a function that shows the #success element only if a particular subview (or its container) is visible (or whatever criterion suits).

// in some suitable outer context
var $success = $('#success');
$success.showConditional = function($container) {
    var $self = $(this);
    return function() {
        if($container.is(':visible')) {
            $self.show();
        }
    }
};

...

$subviewContainer = $("someSelector"); //or maybe the container is already a property of this or this.model?
this.model.fetch().done($success.showConditional($subviewContainer));

With that pattern in place for all fetches, the #success element will not show for hidden or removed subviews.

That's the principle anyway. You may well need to adjust the detail to suit your overall pattern.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • Was thinking the same thing but wasnt sure how to test for a removed object. You're idea of checking the DOM for the cache'd jquery element of the view answered that, but i was worried it would be computationally expensive so instead I did the following: The parent view currently calls this.subviews.cleanup(); when removing the subview. I added and set a field "removed" in the subview's cleanup function: cleanup: function() { this.removed = true; this.remove(); this.unbind(); } And in the ajax call, i did the check: this.model.fetch().always(if(!!self.removed){return;} -code-); – Ta946 Nov 16 '16 at 12:16
  • 1
    Looks cool. You could make `.cleanup()` syntactically neater by returning `true` from `.remove()`, allowing you to write `this.removed = this.remove()`. Actually, that would be useful if it was possible for `this.remove()` sometimes to fail (ie not remove), in which case (maybe with help of try/catch) it could return `false`. – Roamer-1888 Nov 16 '16 at 13:42