-2

My problem is that I need a way of aborting a deferred to stop errors occurring in my application.

I have created a jsfiddle with a simple overview of my problem, which is that a done handler is trying to act on an element that has been removed between the api call starting and it being finished.

Here is an overview of my problem - http://jsfiddle.net/gfNMA/3/

And here how far how I have got

api: function(options) {
    var self = this,
        deferred = $.Deferred(),
        handle = function() {
            if (!self || !self.element) {
                // I need to clear the deferred here
                // E.g deferred.abort()
            }

            deferred[arguments[1] === 'success' ? 'resolve' : 'reject']
                .apply(null, arguments);
        }

    $.ajax(options).always(handle);

    return deferred;
}
Undefined
  • 11,234
  • 5
  • 37
  • 62
  • your demo does nothing, it's just another non-working-at-all version of your code, it's not really a demo. BTW, I suggest trying `.reject` – King King Jun 09 '14 at 02:15
  • By the time `handle()` would be called the request has already taken place, so there's nothing to abort right? – Ja͢ck Jun 09 '14 at 02:16
  • @KingKing I was trying to show a basic overview of the problem I have in my app, i will update it with some more information – Undefined Jun 09 '14 at 02:16
  • @Jack The api call will have finished but the deferred I returned from the api function would still be in a pending state – Undefined Jun 09 '14 at 02:17
  • So, then rejecting the deferred should fix that. – Ja͢ck Jun 09 '14 at 02:18
  • What have you tried? Why didn't that work? Any docs? What effort have you made? – bjb568 Jun 09 '14 at 02:19
  • Rejecting the deferred will mean i get an error as the element that is modified within the done or fail handlers no longer exists. I need to cancel/delete/remove the deferred so it is neither resolved/rejected but no longer exists in memory – Undefined Jun 09 '14 at 02:20
  • @KingKing I have updated the jsfiddle, does that make my question clearer? – Undefined Jun 09 '14 at 02:25
  • @Sam I don't think you can remove the deferred object totally, that's why we called it deferred, there should be some other objects using it somewhere, they can just handle the cases when resolve or reject is called. – King King Jun 09 '14 at 02:26
  • @KingKing I could do something as simple as this -> http://jsfiddle.net/gfNMA/4/ . But it means that my deferred is still running and will become a memory leak if this function is called multiple times – Undefined Jun 09 '14 at 02:33
  • But why do you use `APP.api().always(...` the always handler is always called, both on fail and done, and rejecting the promise leads to a fail callback, which also calls the always handler? This just makes less and less sense to me? Why wouldn't you use `APP.api().done(...`? – adeneo Jun 09 '14 at 02:42
  • @adeneo My question seems pretty hard to describe outside the context of the application it is part of. The APP.api().fail handlers are also using the element that could be removed to show error messages, and will throw the same error as the done handler – Undefined Jun 09 '14 at 02:45
  • Then I guess the only solution is to test whether the element exists or not inside the handlers. – Felix Kling Jun 09 '14 at 02:47
  • Then you're screwed, and you have ... oh Felix beat me, you have to test if the element exists in the handlers – adeneo Jun 09 '14 at 02:47
  • I was trying to avoid this because it could be dozens or even hundreds of different handlers that would need to check whether the element exists. Thanks for the help with this guys – Undefined Jun 09 '14 at 02:49
  • Potentially you could use an interval that keeps checking if the element is still there, and calls abort() on the ajax call, but that's ugly and shouldn't be needed (and unreliable, and probably calls the fail handler as well). The real question is, why to you remove the element in the first place if you're using it ? – adeneo Jun 09 '14 at 02:50
  • An Example would be a JMVC controller on a bootstrap modal. When some content loads and the user closes the modal. The deferred will finish and attempt to access the controllers element using self.element. As the controller has been destroyed by the modal closing it will cause self.element to be undefined in the done/fail handlers – Undefined Jun 09 '14 at 02:54

1 Answers1

1

Deferred objects / promises can't really be aborted. It wouldn't make much sense either, since multiple different branches might depend on the outcome of the promise, and you wouldn't want to "abort" the whole promise only because one branch does not need the result.

Instead, you could wrap that Ajax promise into another promise that tests first whether the element exists or not. If it doesn't not exist, reject the promise.

function ifElementExists(element) {
    return function(result) {
        var deferred = new $.Deferred();
        if (element) { // test if element exists somehow
            deferred.resolve(result);
        }
        else {
            deferred.reject();
        }
        return deferred.promise();
   }
}

api(...).then(ifElementExists(element)).then(doSomething);

Of course you can also test whether the element exists directly in your final callback (without an intermediate promise).

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • @adeneo This is because my application is rather complex with an abstracted api model. So I can not persume a fail handler written by someone else in the team does not act upon an element that does not exist – Undefined Jun 09 '14 at 02:27