Tom
Tom

Reputation: 9127

Mutating arguments in jQuery promises

tl;dr : I'm looking for a way to have the first .then callback make changes to the data that is passed to subsequent chained events.

I have a library that encapsulates some async operations.

dudetools.getDude(2); // causes an XHR against REST resource "Dude" for row id 2

For awesomeness purposes, dudetools.getDude returns the promise created by the underlying $.ajax call. Thus, I can do things like:

dudetools.getDude(dudeId).done(function(dudeData) { /* do stuff with dude's data */ });

Now I'm trying to modify dudetools so that it'll do some convenient data-massaging on response data before continuing along the promise chain. I want this massage to happen universally, without calling code having to request it or even know about it.

Because the dudetools implementation can't share a closure with all calling code, I'm hoping to leverage the fact that, in JavaScript, non-scalars are always passed by reference rather than by value.

Consider:

var urStuff = {};
function wreck(x) {
    x.isWrecked = 'so wrecked';
}
wreck(urStuff);
// urStuff.isWrecked === 'so wrecked' ^.^

I dare you to try it.

So, I was hoping this would work:

dudetools = {
    'getDude': function(dudeId) {
        return $.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
            // I'm so clever!
            dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
        });
    }
}

Of course, it doesn't work. My clever code is being executed (I've seen it), and it's reaching the right conclusions, but subsequent Deferred events in the chain never see the modifications. I.e.:

$.when(
    dudetools.getDude(dudeId)
).done(function(mysteriouslyUnmodifiedInfo) {
    /* the info passed to this function is mysteriously unmodified! HALP */
});

Any suggestions? Is there a way to accomplish what I'm after?

Also: I'm still kind of new to promises in general, and my grasp of the differences between Deferreds, Promises, and associated constructs is still kind of fuzzy, so I'd very much appreciate your efforts to be clear and explicit when explaining to me how I've ruined everything.

Thanks very much.

EDIT: updated to reflect fact that dudetools.getDude returns a promise, not a Deferred. Because I now (mostly) understand the difference.

Upvotes: 1

Views: 144

Answers (2)

LetterEh
LetterEh

Reputation: 26696

The magic of .then is that it pipes its return value into the next callbacks param.

If you don't return your object (even if you haven't changed anything), then undefined is returned by default.

do_something()
 .then(function (json) { return JSON.parse(json); })
 .then(function (response) { return response.data; })
 .then(function (data) { data.tweaked = true; return data; });

Upvotes: 1

Sumit
Sumit

Reputation: 1669

You'll want to return your own new Deferred.promise() object. http://api.jquery.com/deferred.promise/

dudetools = {
    'getDude': function(dudeId) {
        var dfd = new jQuery.Deferred();
        $.ajax('/api/Dude/' + dudeId).then(function(dudeData) {
            dudeData.isDuplicated = dudeData.isDuped && dudeData.drillDown > 5;
             // I'm so clever!
           dfd.resolve(dudeData);
        });

        return dfd.promise();
    }
}

Hope that helps.

Upvotes: 0

Related Questions