0

I am new to both promises and having trouble understanding what I need to do to code the following logic:

I am developing a Web service in Node.js and Express to fetch song data from a wiki and return an object, which a client application will consume. The wiki's API does not let me write a batch query; I have to get each page individually. So I will have to get the list of songs, then perform a call for each song.

I currently intend to use the Q middleware for Node.js as my promises library, though I am open to suggestions on a more appropriate middleware for this task.

Here is my pseudocode:

app.get('/songs/:criteria', function(request,response) {
    downloadSongList()
    .then(foreach(song) downloadSongData)
    .then(assembleReturnValue)
    .then(response.json(returnValue));
});

What will the actual code look like?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Jay Bienvenu
  • 3,069
  • 5
  • 33
  • 44

2 Answers2

3

The actual code will use function expressions, and around the foreach you will need to use Q.all:

app.get('/songs/:criteria', function(request,response) {
    downloadSongList(request.params)
    .then(function(list) {
        var promises = list.map(function(song) {
            return downloadSongData(song.params) // another promise
        });
        return Q.all(promises);
    }).then(function(allResults) {
        // assemble
        return // Value;
    }).then(response.json);
});

Also have a look at these general rules for promise development.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Where does a .catch() fit in here to catch an error? – Jay Bienvenu Sep 25 '14 at 20:56
  • Depends on what error you want to catch and whether you plan to recover from it, but usually in the end to catch all of them. – Bergi Sep 26 '14 at 10:02
  • I was premature in accepting this answer, because it does not work properly. Instead of returning the JSON in the response, the server sits on the last step until it times out. I tried adding .done() at the end of the chain; that doesn't work. – Jay Bienvenu Sep 26 '14 at 11:15
  • Add another `.catch(console.log)` at the end to see what fails. My guess would be that `.json()` does require to be invoked as a method, try `.then(response.json.bind(response))` or a function expression instead. If that is not the problem, you probably should still accept this answer and [ask a new question](http://stackoverflow.com/questions/ask) where you'd post your current, actual code. – Bergi Sep 26 '14 at 12:48
0

Here is an alternative solution with Bluebird, since you said you were interested in different libraries:

  downloadSongList(request.params).
                 map(downloadSongData).
                 call("join",",").
                 then(response.json).catch(sendError)

What we're utilizing here:

  • .map - which takes an array of promises, and calls a method on each one of them, we do that for the list returned from downloadSongList.
  • .call which calls an array method, here we're joining the elements as strings, not sure what format you're using here but this will basically do array.join.

These are some advantages we gain from Bluebird, other than that this is very similar to Bergi's answer.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504