1

The scenario is as following:

  • a call is made to the server that returns an array of objects if everything is fine.
  • if an error occurs on the server, it returns a json that contains an error message.

Out of this scenario, I want to create a function that returns a promise, which I can use like this from a client:

initData()
    .done(function(myArray) {
        processData(myArray);
    })
    .fail(function(response) {
        console.log("An error occured: " + response.errorMessage);
    });

The client should not care that initData() makes a call to a server, it should get the fail() called for both ajax call failures or server specific errors.

For this I need two things:

  1. Hide the failure of the server call and create a new error message object.
  2. Make the promise fail if the call to the server returned with an error message object.

Here's what I have:

function initData() {
    var promise = $.getJSON(url);

    return promise.then(function(response) {
        if (typeof response !== 'undefined' && response !== null && typeof response.errorMessage !== 'undefined') {
            // Do something to trigger fail() in the client caller.
        }
        return response;
    }, function(resp) {
        // This satisfies point 1 above.
        return { errorMessage: "Server error." };
    });
}

How may I satisfy the second requirement? I'm using jquery 3.1.1.

Thanks.

Paul
  • 1,224
  • 2
  • 14
  • 31

2 Answers2

1

Just throw an error from your then callback. The caller will "catch" it in their fail callback (though I suggest you learn to use standard then and catch and stop using jquery-specific done and fail).

function initData() {
    var promise = $.getJSON(url);

    return promise.then(function(response) {
        if (typeof response !== 'undefined' && response !== null && typeof response.errorMessage !== 'undefined') {
            throw new Error("uh oh!");
        }
        return response;
    }, function(resp) {
        // This satisfies point 1 above.
        throw new Error("server error");
    });
}
Brandon
  • 38,310
  • 8
  • 82
  • 87
  • Thanks. Could you please elaborate a bit on the recommended alternative? – Paul Feb 06 '17 at 19:33
  • 1
    Just stop using `done` and `fail` -- they are jQuery-specific and before jQuery updated their implementation to match the Promise spec. Instead call `then` and `catch` which are the spec-compliant functions. They mostly work the same so you could change your "client code" to from `.done(...).fail(...)` to `.then(...).catch(...)` and it would work (and also work with a standard Promise) – Brandon Feb 06 '17 at 19:40
  • I was insisting because I'd found a solution before posting the question, by using deferred objects, but I already had the question written down and was curious about alternatives. I'd already tried using throw following a friend's suggestion, but to my despair it didn't work, because I was using jquery 1.11.3 in the beginning, which doesn't support this. Regarding your answer, the line `return { errorMessage: "Server error." };` doesn't work as expected when using then/catch. I believe `throw` is mandatory in that case. – Paul Feb 06 '17 at 21:23
  • gotcha. I fixed that bit. And yes, earlier version of jQuery deferred's (before jQuery 3.0) were not compliant with the Promise spec. I did not qualify this answer since your question specifically stated jQuery 3.1.1. – Brandon Feb 07 '17 at 19:55
1

To fail the promise, simply throw the result instead of returning it:

function initData() {
    return $.getJSON(url)e.then(function(response) {
        if (typeof response !== 'undefined' && response !== null && typeof response.errorMessage !== 'undefined') {
            throw { errorMessage: "Response error." };
        }
        return response;
    }, function(resp) {
        throw { errorMessage: "Server error." };
    });
}

(This works only as of jQuery 3. For earlier versions, see Throwing an Error in jQuery's Deferred object)

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375