1

Here is an example of a working code:

request('http://foo.com/')
    // Collect list of locations.
    .then(function (response) {
        let $ = response.data,
            locations = [];

        $('a[href^="/venues/"]').each(function () {
            locations.push({
                name: $(this).text(),
                url: 'http://foo.com/' + $(this).attr('href')
            });
        });

        return locations;
    })
    // Retrieve additional information about locations.
    .map((location) => {
        return request(location.url)
            .then((response) => {
                // Combine location specific information with the initial location information.
                response;
                location;
            });
    }, {
        concurrency: 5
    });

In the above example, I am making a request to "foo.com", get a list of locations, then use location data to query "foo.com" for additional information about the location. At the end, I combine the location specific information with the initial location information.

What I do not like about the above the above code is that it is not flat. If location specific query would require additional async information and it child would require even more additional async information, the code would turn into a callback hell.

I would like to turn the code into a flat structure, e.g. (pseudo-code)

request('http://foo.com/')
    // Collect list of locations.
    .then(function (response) {
        let $ = response.data,
            locations = [];

        $('a[href^="/venues/"]').each(function () {
            locations.push({
                name: $(this).text(),
                url: 'http://foo.com/' + $(this).attr('href')
            });
        });

        return locations;
    })
    // Retrieve additional information about locations.
    .map((location) => {
        return request(location.url);
    }, {
        concurrency: 5
    })
    // Filter list of responses.
    .filter((response) => {
        return response.incomingMessage.statusCode === 200;
    })
    // Combine location specific information with the initial location information.
    .map((response) => {
        // How to access "location" and "response"?
    });

What is the best way to achieve it?

Gajus
  • 69,002
  • 70
  • 275
  • 438
  • You're doing a loop over an array. Why would you expect that to be flat? – Bergi Oct 26 '15 at 17:34
  • Related, but without the loop: [How do I access previous promise results in a .then() chain?](http://stackoverflow.com/q/28250680/1048572) – Bergi Oct 26 '15 at 17:39

2 Answers2

0

The only way I could think of doing it was by augmenting the response of the request to get additional data about a location, e.g.

.map((location) => {
    return request('http://foo.com/' + location.nid)
        .then((response) => {
            return {
                location: location,
                response: response
            };
        });
}, {
    concurrency: 5
})

This way the entire Promise chain remains more or less flat.

request('http://foo.com/')
    // Collect list of locations.
    .then(function (response) {
        let $ = response.data,
            locations = [];

        $('a[href^="/venues/"]').each(function () {
            locations.push({
                nid: $(this).attr('href'),
                name: $(this).text(),
                url: 'http://foo.com/' + $(this).attr('href')
            });
        });

        return locations;
    })
    // Retrieve additional information about locations.
    .map((location) => {
        return request('http://foo.com/' + location.nid)
            .then((response) => {
                return {
                    location: location,
                    response: response
                };
            });
    }, {
        concurrency: 5
    })
    // Filter list of responses.
    .filter((response) => {
        return response.response.incomingMessage.statusCode === 200;
    })
    .map((response) => {
        // Combine location specific information with the initial location information.
        response.response;
        response.location;
    });
Gajus
  • 69,002
  • 70
  • 275
  • 438
0

If you do a bind({}) after request(), the functions in the chain will have a this object that you can use to store things.

Example:

Promise.resolve('A').bind({}).then(function(value){
    this.value = value;
    return 5;
}).then(function(number) {
    this.number = number;
}).then(function() {
    console.log(this);
})

Output:

{ value: 'A', number: 5 }
cleong
  • 7,242
  • 4
  • 31
  • 40