1

It doesn't seem like I can return a deferred object if it failed. I have a pretty nested ajax request that goes into a queue so I need a way to return the request as a deferred object. Everything is great if the request is successful, but I am stuck on getting failures to propagate.

https://jsfiddle.net/3wtady9r/

function doAjax(file) {
    var defer = $.Deferred();
    var fakeFile = file;
    var data = null;

    ajax();

    function ajax() {
        return $.get(fakeFile)
            .done(function(data) {
                data = data;
                defer.resolve(data);
            })
            .fail(function() {
                defer.fail();
            })
    }
    return defer.promise(data);
}

var thisWillWork = '';

doAjax(thisWillWork)
    .done(function() {
        console.log('done')
    })
    .fail(function() {
        console.error('fail')
    })
    .always(function() {
        console.log('always')
    })

var thisWontWork = 'fakeFile.html'
doAjax(thisWontWork)
    .done(function() {
        console.log('done')
    })
    .fail(function() {
        console.error('fail')
    })
    .always(function() {
        console.log('always')
    })

When I do doAjax(thisWontWork) I am expecting to get the console error and also .always, but neither fire.

BarryBones41
  • 1,361
  • 1
  • 13
  • 30
  • 1
    Some strange stuff. You're calling `ajax()` and ignoring the return value. You have `data = data;`, but that only assigns to the parameter, not the `var data = null`, so the `defer.promise(data)` is still passing `null` –  Feb 17 '16 at 17:17
  • This looks like a horribly failed attempt to implement the [deferred antipattern](http://stackoverflow.com/q/23803743/1048572) to me. Don't do that. – Bergi Feb 17 '16 at 17:20

2 Answers2

2

There are a bunch of different issues there.

  1. fail is a method for hooking up handlers to get called if the promise is rejected. To reject it, use reject.

  2. You don't want to pass data into defer.promise at the end.

  3. You don't want or need a data variable at all, just the argument you receive in success.

  4. Since you never use the value you return from your own ajax function, there's no need to return it.

  5. jQuery's ajax already returns a promise that does what your promise does, so your promise can just be removed entirely. This happens often enough there's an antipattern named for it. Every time you think you need to create a promise, stop yourself and think: Do I already have one? Sure, sometimes the answer is no, but often it's yes. :-)

As of point #5, we end up with:

function doAjax(file) {
    var fakeFile = file;

    return $.get(fakeFile);
}

...which means, of course, that you don't need doAjax at all. Just call $.get.

But if for some reason you haven't shared you need your own promise, then just points 1-4:

function doAjax(file) {
    var defer = $.Deferred();
    var fakeFile = file;

    ajax();

    function ajax() {
        $.get(fakeFile)
            .done(function(data) {
                defer.resolve(data);
            })
            .fail(function() {
                defer.reject();
            });
    }
    return defer.promise();
}
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • This is a really simplified version of what I have going on. This function is actually passed into a queue so it needs to be returned like this. Thank you for the fixes. – BarryBones41 Feb 17 '16 at 17:59
  • @BarryBones41: Glad that helped. At the end of the day, it doesn't matter if this is in a queue or something, if it returns a promise that gets fulfilled with `data` or rejected based on the result of the `$.get`, then you can return the promise from `$.get`. – T.J. Crowder Feb 17 '16 at 18:21
1

It should be defer.reject(); not defer.fail();

Also you can add data in the reject like you did in defer.resolve().

Adrian
  • 1,597
  • 12
  • 16