Search code examples
ajaxtypescripttransformchaining

In a chain of $.ajax().done() calls, can I transform data for next .done()?


I've got an existing api routine that I'd like to insert transform before the next .done() gets called. In my case it a simple extra level of JSON.parse() that needs to occur but the wider question is can I do anything to the response data that the next .done() will be able to see?

so take this api :

function foo(){
   return $.ajax( ... )
           .done(data=> {  .. do something with the data here ...});
}

and the caller can see this:

foo().done(data=>{ .. transformed data visible here ... });

Solution

  • jQuery's ajax returns a Deferred object, which is (now) a promise per the Promises/A+ specification. So you could switch to using then, which has the semantics you're looking for:

    function foo(){
       return $.ajax( ... )
               .then(data => {
                   return /*...the transformed data...*/;
               });
    }
    

    then:

    foo().then(data => { /*.. transformed data visible here ...*/ });
    

    Note: If the code calling foo can't be changed, no worries; done will see the updated data, too. But it has to be then within foo. Switch to then in the code calling foo too if you can, but if you can't, it's fine.

    Here's a live example using then within foo and done when calling it: https://jsfiddle.net/tjcrowder/y1zh2xL4/

    function foo(){
        return $.ajax({
            method: "POST",
            url: "/echo/json/",
            data: {
                json: '{"key":"value"}',
                delay: 1
            }
        })
        .then(data => {
            return {key: data.key.toUpperCase()};
        });
    }
    
    // Just to show that `done` sees the updated data
    foo().done(data => { console.log(data); });
    

    In a comment you asked:

    Further question; is there a way to do the same sort of transform in an error chain?

    Yup! Promises are designed such that the two paths can be transformed at each level. To transform the rejection passing through the chain, you either return a rejected promise (return Promise.reject(x)) or throw (throw x). x can be anything you want, although by convention it's usually an Error object.

    So for instance (https://jsfiddle.net/tjcrowder/y1zh2xL4/1/):

    function foo(){
        return $.ajax({
            method: "POST",
            url: "/echo/bad-url-not-on-jsfiddle",
            data: {
                json: '{"key":"value"}',
                delay: 1
            }
        })
        .then(data => {
            return {key: data.key.toUpperCase()};
        })
        .catch(error => {
            const myError = new Error("ajax failed");
            myError.originalError = error;
            myError.otherInformation = "whatever";
            throw myError;
        });
    }
    
    foo()
    .done(data => { console.log(data); })
    .fail(error => {
        console.error(error);
        console.error(error.otherInformation);
    });