3

I have a function called fetchCourseCode(string) that fetches some information from a web server and stores them in to array. I also have ~135 course codes (strings) that should be fetched in discrete times with same period.

Normally, in a non-asynchronous situation, the code should be:

for (eachCode of COURSE_CODES) {
    fetchedInformationArray.push(fetchCourseCode(eachCode));
}

However, as this is a time taking operation, node.js event loop escapes to the fetchCourseCode()'s return and we got nothing (an array of undefineds) in the end.

So I tried to use async's forEachOf function to iterate over array, having constructed a promise manually to the fetchCourseCode() function:

async.forEachOf(kCOURSE_CODES, function(value, key, callback) {
    console.log("fetched " + value);

    fetchCourseCode(value).then(response) {
        schedule[key] = response;

        callback();
    }, function(error) {
        callback(error);
    });     
}, function(error) {
    if (error) {
        console.error(error);
    } else {
        console.log("Fetched schedule: " + JSON.stringify(schedule));
    }
});

Unfortunately I found out it is said that using nested promises is an anti-pattern.

I am -desperately thinking of being- not able to use Q.all(), because the list of functions is dynamically changing. Also, async.map couldn't help me: Callback function is not called strangely.

I haven't got to understanding of node's event loop, being pretty new in these lands.

Buğra Ekuklu
  • 3,049
  • 2
  • 17
  • 28
  • How to make a for `lo` ? ... Do you mean `loop` – l2aelba Sep 28 '15 at 09:11
  • @l2aelba weirdly title was not shown, fixed it however. – Buğra Ekuklu Sep 28 '15 at 09:13
  • how are you getting the course codes? – chriskelly Sep 28 '15 at 09:26
  • @chriskelly with an HTTP request, followed by some parsing. – Buğra Ekuklu Sep 28 '15 at 09:36
  • @Leviathlon: if you are using ES6 it sounds like you should convert the incoming data into an iterable collection and use ES6 "for of" to pseudo-synchronously loop over your data. Is this an option with your node version? – chriskelly Sep 28 '15 at 09:48
  • @chriskelly I believe it is ES6 with some constraints on it, do you say an array is not originally iterable, which means that I ought to mutate to iterate over it? – Buğra Ekuklu Sep 28 '15 at 10:31
  • 1
    @Leviathlon: Arrays are iterable but I was thinking about [Symbol.iterator] which is required by "for of" loops. I think you would need something like a generator to produce new items which are iterable using "for of" but maybe I am wrong. I would like to give it a go in a few hours when I have some time. This would be a nice way to handle those types of situations. You could also handle it by making a new loop each time your request receives new courses but "for of" is definitely a nicer approach I think. – chriskelly Sep 28 '15 at 11:11
  • For the record, the array returned from function contains string arrays. Hence, `schedule` should be an array of arrays that contain string arrays. – Buğra Ekuklu Sep 28 '15 at 11:28
  • "*I found out it is said that using nested promises is an anti-pattern.*" - not exactly, no, there are many cases where you need nesting. What exactly did you read, can you link it? The antipattern I can see in your code is rather that you use the `async` module when you have promises available. – Bergi Sep 28 '15 at 11:43
  • "*I am not able to use Q.all(), because the list of functions is dynamically changing*" - how is it changing? Can you show us that please? Does it really change during the (asynchronous) iteration? That would be an odd pattern even with a synchronous loop. – Bergi Sep 28 '15 at 11:44
  • @Bergi, for the latter one, no, it is certain that the array is a constant during async run. – Buğra Ekuklu Sep 28 '15 at 11:50
  • @Bergi, and here is the link where I found that information: http://taoofcode.net/promise-anti-patterns/ – Buğra Ekuklu Sep 28 '15 at 11:52
  • 1
    @Bergi it also seemed logical to be an anti-pattern. I will be glad if you give an illustration of when it's good to use them. – Buğra Ekuklu Sep 28 '15 at 11:56
  • @Leviathlon: Well, in that case you trivially can use `Q.all`, like in mido22's answer. – Bergi Sep 28 '15 at 11:57
  • 1
    @Leviathlon: Well, there are some cases where some kinds of nesting are an antipattern, but it's not bad per se. In the case you linked it certainly is (as `loadAnotherthing()` doesn't depend on `loadSomething()`), but nesting is just a technique and often necessary (typically for looping and branching). Often enough it's [just a solution](http://stackoverflow.com/a/28250687/1048572) equally good as many others. And btw, given that you were using `async`, not some promise function, you weren't even nesting promises. – Bergi Sep 28 '15 at 12:02
  • @Bergi but use of `async` and a promises library aren't heuristically similar? – Buğra Ekuklu Sep 28 '15 at 12:08
  • @Leviathlon: Yeah, they're similar in their purpose, but due to the different callback convention they don't work together well, requiring a lot of ugly manual plumbing code. – Bergi Sep 28 '15 at 12:13

1 Answers1

1

I am assuming that you are using the latest node, then if you have an array of COURSE_CODES, you could simply do:

Promise.all(COURSE_CODES.map(fetchCourseCode)).then(results => ...)
mido
  • 24,198
  • 15
  • 92
  • 117
  • That gave me an array of `undefined`s likewise I have had so far: `[ undefined, undefined, undefined,..., undefined ]` (logged output in `then` block of your code block) – Buğra Ekuklu Sep 28 '15 at 21:06
  • @Leviathlon, you are getting array of `undefined` because each of the `fetchCourseCode` resolves to value of `undefined`, modify that method to return the desired value... – mido Sep 29 '15 at 01:15
  • 1
    `Q` couldn't but `Promises/A+` solved my issue. Thank you. – Buğra Ekuklu Sep 29 '15 at 05:13