6

I have some Javascript code doing some asyncronous stuff with some synchronouse post processing and then asyncronous stuff again (XHR -> parse XHR -> new XHR based on the first one). I don't get the error handling done:

/* API */
function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        return data.id
    }, handleError)
}

function handleError(errorObj) {
    if (errorObj.status) {
        return errorObj.status
    } else {
        return errorObj
    }
}

function myApiCall() {
    return getFile(1)
        .then(getFile, handleError)
        .then(getFile, handleError)
        .then(getFile, handleError)
        .then(getFile, handleError);
}


/* caller */

function failFunction(status) {
    console.log("fail: ")
    console.log(status)
}

myApiCall().then(function(id) {
        console.log(id)
    }, failFunction)

1.json looks something like this

{
    "id": "2"
}

Just referencing the next file, the other ones are equivalent.

Until here everything is fine (even I'm not sure if that's the best way to do errorhandling). If all files exist, the success function of the caller is called, otherwise the error function.

But when having some errors in my synchronous code, everthing breaks

function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        throw new Error(42) //       <---------
    }, handleError)
}

Now I get a console output

Error: 42

No further processing, caller is not informed.

I tried something like that

function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        throw new Error(42)
    }, handleError)
    .catch(handleError)
}

But this doesn't make anything better. Mostly of all I get a TypeError: $.ajax(...).then(...).catch is not a function

How is error handling solved correct in this case?

1
  • 2
    if you return a value from a catch or error handler, the error will not propagate - if you want it to propagate, either throw the error or return a Promise.reject. The fact that jquery functions may be messing up the promise chain isn't a surprise, jquery promises are not (pre v3 at least) truly Promise/A+ compliant Commented Nov 28, 2015 at 5:17

2 Answers 2

11

Because you are handling errors at every stage, and not propagating them, then you will end up with the problems you are having

edit:

The above statement is wrong, I didn't realise how broken jquery promises are when I wrote it - see the difference between the first block of code (Promise/A+) and the second (jQuery promise with fingers crossed behind it's back)

instead you can do something like:

function getFile(name) {
  // wrap jquery "promise" in real Promise
  // you'll need a Promise polyfill for Internet Explorer to use this method
  return new Promise(function(resolve, reject) {
    $.ajax({
      url: name + ".json?" + Math.random()
    }).then(function(data) {
      resolve(data.id);
    }, function(err) {
      reject(err);
    });
  });
}

function handleError(errorObj) {
  //console.log(errorObj)
  if (errorObj.status) {
    throw errorObj.status;
  } else {
    throw errorObj;
  }
}

function myApiCall() {
  return getFile(1)
  .then(getFile)
  .then(getFile)
  .then(getFile)
  .then(getFile)
  .catch(handleError);
}

function failFunction(status) {
  console.log("fail: ")
  console.log(status)
}

myApiCall().then(function(id) {
  console.log(id);
})
.catch(failFunction);

When using jQuery, whose promises are like promises you make with your fingers crossed behind your back

function getFile(name) {
    return $.ajax({
        url: name + ".json"
    }).then(function(data) {
        return data.id;
    });
}
function handleError(errorObj) {
    if (errorObj.status) {
        return errorObj.status; // return in jquery!
    } else {
        return errorObj;
    }
}
function myApiCall() {
    return getFile(1)
        .then(getFile)
        .then(getFile)
        .then(getFile)
        .then(getFile)
        .fail(handleError); // like jquery promises in general, use fail instead of the compliant catch
}
/* caller */
function failFunction(status) {
    console.log("fail: ");
    console.log(status);
}
myApiCall().then(function(id) {
    console.log(id);
})
.fail(failFunction);
Sign up to request clarification or add additional context in comments.

7 Comments

Did you try that out? Pasting your code has for me the effect, that the error isn't translated into a statuscode and throwing new Error(42) still results in uncatched exceptions...
Did you try that out? - as much as I could without access to your server and files!! the error isn't translated into a statuscode ... does errorObj have a .status - console.log it to find out ... as far as uncaught exceptions go, you're using jquery, good luck making it work like Promises should
I just tested the jQuery version (the first version will not work if you use jquery crossed fingers behind the back promises) and it works fine using .fail etc
I've updated the first block of code to wrap jquery piss poor attempt at promise with a real promise - this way, however, you'll need a Promise polyfill (or some other library that provides some sort of Promise that isn't jquery) to make it work in Internet Explorer ... all code in the answer has been tested and works as expected in both non-error and error conditions to the best of my abilities to "simulate" errors If it doesn't work for you, you probably have a caching issue, or you're doing something else completely wrong
Please avoid the promise constructor antipattern and just use Promise.resolve($.ajax(…)). jQuery is at least thenable in A+ terms.
|
0

To all of you fed up with promises when using jQuery. Promises are real cool; jQuery promises suck. Here some more examplecode based on Jaromandas answer, for anyone who can use it.

/* API */
function getFileWithError(name) {
    return getFile(name, true)
}

function getFile(name, doThrow) {
  if (name == null) {
    console.log("handled null as return from chain before")
    name = 2
  }
  return wrapJquery(
    $.ajax({
      url: name + ".json?" + Math.random()
    })
  ).then(function(data) {
    if (data.id == 2) {
        return null
    }
    if (doThrow) {
        throw data.id
    }
    return data.id
  });
}

function wrapJquery(crossingFingersPromise) {
    return new Promise(function(resolve, reject) {
        crossingFingersPromise
        .then(function(data) {
          resolve(data);
        }, function(err) {
          reject(err);
        });
     })
}

function handleError(errorObj) {
    if (errorObj.status) {
        throw errorObj.status; // return in jquery!
    } else {
        throw errorObj;
    }
}

function myApiCall() {
    return getFile(1)
        .then(getFile)
        .then(getFileWithError)
        .then(getFile, function(error) {
            console.log("recovering from error")
            return getFile(error)
        })
        .then(getFile)
        .catch(handleError)
}
/* caller */
function failFunction(status) {
    console.log("fail: ");
    console.log(status);
}
myApiCall().then(function(id) {
    console.log(id);
})
.catch(failFunction);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.