0

I'm new to the when.js javascript library, but I'm familiar with async programming in C#. That's why I find this code to be unwieldy:

filters.doFilter('filter1name', reqAndPosts).then(function(filter1) {
    filters.doFilter('filter2name', filter1).then(function(filter2) {
        filters.doFilter('filter3name', filter2).then(function (posts) {
            renderView(posts); 
        });
    });
    return filter1;
});

I basically want three methods to be called in sequence, with the output of each being piped to the next method. Is there anyway I can refactor this code to be more "sequence-like" - i.e. get rid of the nesting? I feel like there's something I'm missing with the when-framework here. I'm not doing it right, right?

Nilzor
  • 18,082
  • 22
  • 100
  • 167

3 Answers3

3

Since doFilter returns a promise, we can do

filters.doFilter('filter1name', reqAndPosts)
    .then(function(filter1) {
        return filters.doFilter('filter2name', filter1);
    })
    .then(function(filter2) {
        return filters.doFilter('filter3name', filter2);
    })
    .then(renderView);
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • 1
    Nilzor, while your present approach will make the result of a previous operation available in all later operations (scope), @thefourtheye method results in cleaner and easier to read code. Both have different suitable scenarios. – Akash May 06 '14 at 07:06
  • Yea. Cleaner code was what I was looking for now, so this is it. Thanks. – Nilzor May 06 '14 at 07:38
  • @AkashAgrawal no, as my answer shows, there are better ways :) – Florian Margaine May 06 '14 at 08:27
3

There is another option to have both advantages of cleaner indentation and previous results available: using withThis.

filters.doFilter('filter1name', reqAndPosts).withThis({}).then(function(filter1) {
    this.filter1 = filter1; // since we used withThis, you use `this` to store values
    return filters.doFilter('filter2name', filter1);
}).then(function(filter2) {
    // use "this.filter1" if you want
    return filters.doFilter('filter3name', filter2);
}).then(renderView);
Florian Margaine
  • 58,730
  • 15
  • 91
  • 116
1

With a little thought you can write a generalised utility function that will take a start object and a filter sequence as its arguments, dynamically build the required .then chain, and return a promise of the multi-filtered result.

The function will look like this ...

function doFilters(filterArray, startObj) {
    return filterArray.reduce(function(promise, f) {
        return promise.then(function(result) {
            return filters.doFilter(f, result);
        });
    }, when(startObj));
}

... which is an adaptation of a pattern given here in the section headed "The Collection Kerfuffle".

For the operation you want, call as follows :

doFilters(['filter1name', 'filter2name', 'filter3name'], reqAndPosts).then(function(result) {
    //All filtering is complete.
    //Do awesome stuff with the result.
});

Provding it is not destroyed and is in scope, doFilters() will remain available to be used elsewhere in your code :

doFilters(['f1', 'f2', 'f3'], myOtherObject).then(function(result) {
    //...
});

With very little more effort, you could tidy things up by phrasing doFilters() as a method of filters. That would be best of all.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
  • Nice. This is more or less what `when.pipeline()` does as well, right? I'm beginning to get a grasp on promises in Javascript, and your post definitely helped in that regard. https://github.com/cujojs/when/blob/master/docs/api.md#whenpipeline – Nilzor May 06 '14 at 12:20
  • Nilzor, I'm not really a `when` person - most of my Promise experience lies in jQuery - but from what I understand `when` offers three related methods `when.reduce`, `when/sequence`, and `when/pipeline` as "great ways to process asynchronous arrays of promises and tasks". Without trawling through the doc', I'm not sure which is most relevant but yes possibly `when/pipeline`. I used JS's native `array.reduce()` which is fast becoming the preferred method. Environments with < ECMAScript 5.1 (JS 1.8) need either a polyfill for the native `.reduce` or a lib equiv' such as `when` appears to offer. – Roamer-1888 May 06 '14 at 17:03