19

I've been reading about deferreds and promises in jQuery but I haven't used it yet.

I've understood everything very well but the method pipe. I really didn't get what it is.

Could some please help me to understand what it does and where could it be used?

I know there is a question titled exactly like this one (here) but its not the same. I'm asking for help to understand it and some example. The objective of the other question is to get why it doesn't work in a particular case.

Community
  • 1
  • 1
Diego
  • 15,566
  • 24
  • 79
  • 132

3 Answers3

47

Basically, Deferred.pipe() is an asynchronous equivalent to $.map(). It projects new values from other values provided as input, but its purpose is to be used with continuations.

Let's start with an example that only requires $.each() and issues an AJAX request that returns a simple object. For each property of this object, we want the form control whose id attribute is the property's key to set its value to the property's value. We can write something like:

$.ajax("your/url", {
    dataType: "json"
}).done(function(data) {
    $.each(data, function(key, value) {
        $("#" + key).val(value);
    });
});

Now let's say we want to apply some function to the values before updating the form controls. If we do that locally, we only have to write:

$.ajax("your/url", {
    dataType: "json"
}).done(function(data) {
    $.each(data, function(key, value) {
        // doSomethingWith() projects values synchronously, as map() does.
        $("#" + key).val(doSomethingWith(value));
    });
});

But what happens if doSomethingWith() is not implemented client-side, but server-side through another web service? In that case, we want to chain the control flow into the second AJAX request, and only update the form controls when the second request has returned. Deferred.pipe() makes that easy:

$.ajax("your/url", {
    dataType: "json"
}).pipe(function(theOriginalData) {
    return $.ajax("your/web/service/doSomethingWith", {
        data: theOriginalData,
        dataType: "json"
    });
}).done(function(theFinalData) {
    $.each(theFinalData, function(key, value) {
        $("#" + key).val(value);
    });
});
Frédéric Hamidi
  • 240,249
  • 39
  • 455
  • 462
  • 1
    Are these 2 examples actually functionally the same? This notion that the anon function which calls the webservice inside the `.pipe` call, *iteratively*, is new to me. I know that as of jQuery 1.8, the `.pipe` method is deprecated and replaced by `.then`, but could you elaborate on the functional differences between the `.each` & `.pipe` version, if they exist? How is the return data from the first GET enumerated by `.pipe`? If this is not how it works, I think it would be helpful to make clear that the `.pipe` version calling a webservice works as a batch process, not iteratively. – JoeBrockhaus Feb 19 '15 at 16:54
  • @Joe, the examples are "functionally" the same, only the first one processes the values synchronously, whereas the second one does that asynchronously (through a web service). `pipe()` (and now `then()`) is only called when the first request has returned, so the original data is already available. The code *looks* iterative, but `pipe()` returning a promise means the rest of the pipeline will wait for that promise to be fulfilled (or rejected). – Frédéric Hamidi Feb 19 '15 at 18:55
  • I meant that in the initial version, each value in the specially-crafted JSON was being run through `doSomethingWith`. In the `.pipe()` method, the entire specially-crafted JSON is sent back to the server, where the server iterates the items and applies the transformation, and then sends it back, and the final `.done` iterates those items. AKA, if it were identical, `$.ajax("your/web/service/doSomethingWith"` would be called multiple times, which is not the case in the `pipe` method, right? AKA, `pipe` doesn't magically iterate the original JSON - it passes the whole chunk of JSON? – JoeBrockhaus Feb 19 '15 at 19:41
  • 1
    @Joe, absolutely. Performing one request to the web service for every key/value pair in the data would be very inefficient. Better let the web service process the data in bulk. – Frédéric Hamidi Feb 19 '15 at 19:57
  • I agree, just wanted to point that out since the answer inferred it was identical (and I was genuinely curious if there *was* some magic happening). Also, for someone trying to grasp promises/deferreds for the first time (and maybe basic jQuery alike, and especially because it's JavaScript and it's not obvious what exactly would be passed around), it can be difficult to compare if it's assumed everything is the same, when its subtly different. – JoeBrockhaus Feb 20 '15 at 00:00
  • I don't understand how this can be functionally the same, when last example is making another AJAX call. – Martin Zvarík Aug 24 '20 at 22:28
11

OK, I see a lot of reference material in another answer here, but reading is sometimes not the same as understanding.

I find it easiest to think of a Promise and the application of .done() to it vs. .pipe() to it. Each one acts differently. If I take promise.done(function (result) { ... }) then I can tack on more .done()'s or .fail()'s after that because each call to .done() or .fail() returns the exact same promise. So each function will be tied to the original promise and whether it gets resolved or rejected.

Now, contrast that to .pipe(). If I take promise.pipe(function (result) { ...}) then what comes out of the .pipe() is an all new promise! If I then attach .done() or .fail() to that promise then those functions will get the modified version of the results that the .pipe() returns, not the original results.

So .pipe() is, in my experience, rarely necessary. The only time it really comes in handy is if you need to modify the data that is returned from a promise before other code sees it (for example, modifying some results of an AJAX call on the client side before any other client side code works with it) or if you need to sequence things. For example, after promise A resolves or rejects, take another action, and then only when that is done do we want other code to finally trigger. All of the other code is attached to the promise B that came from the .pipe() call.

Here's a recent question where another user had problems with the use of promises and .pipe()/.done()/.when() and I tried to provide some code to clarify the use of each one in a jsFiddle: Do something when all deferreds are resolved

Community
  • 1
  • 1
John Munsch
  • 19,481
  • 8
  • 40
  • 72
1

Hiya is this what you are looking for :)

[nice read] http://www.bennadel.com/blog/2255-Using-jQuery-s-Pipe-Method-To-Change-Deferred-Resolution.htm

quote

The pipe() method provides a filter for both the success and failure resolutions (of the AJAX request). If the original resolution is success, the pipe() filter either passes a truly successful response through; or, it changes the resolution, returning a new rejected promise. Then, if the original request was a failure, which would be truly unexpected in our API, the pipe() filter simply passes through a normalized API response structure....

Stack link with example Understanding jQuery Deferred.pipe() (has jsfiddle in it)

Understanding Deferred & Promise? please see here http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

Jquery official API page http://api.jquery.com/deferred.pipe/ (with examples)

Description: Utility method to filter and/or chain Deferreds.

The deferred.pipe() method returns a new promise that filters the status and values of a deferred through a function. The doneFilter and failFilter functions filter the original deferred's resolved / rejected status and values. As of jQuery 1.7, the method also accepts a progressFilter function to filter any calls to the original deferred's notify or notifyWith methods.

Tats_innit
  • 33,101
  • 9
  • 67
  • 75