1

Suppose I have the following source code in NodeJS using promise (connecting to mongodb with 3 collections, well, this is not important)

function getData(){
    return new promise((resolve, reject) => {
        var dataArray = [];
        getCollection1().then((rows1) => {
            getCollection2().then((rows2) => {
                var val = someDataProcessingFunction(rows1, rows2);
                dataArray.push(val);

                resolve(dataArray);
            }, (err) => {
                reject(err);
            }
        }, (err) => {
            reject(err);
        });

        getCollection3().then((rows3) => {
            rows3.forEach((row3) => {
                dataArray.push(row3);
            });
            resolve(dataArray);
        }, (err) => {
            reject(err);
        });
    });
}

The problem is, there is multiple places where dataArray is pushed, but I don't know when all the pushing is completed. so that I don't know when to resolve, to return the data to user, for example, by response.send in express. As to the code above while I am using resolves several times, I will only get part of the whole data returned.

I can think out two workarounds, neither is good. one is to surround all resolves in setTimeout, which means I must delay the response to user.

another is to count all the push times at the beginning, every time subtract 1 when one item is pushed. when the times goes 0, do resolve. but it is often difficult to calculate the number before doing the real loops.

Is there better solutions? Thank you and love you :)

vdonkey
  • 106
  • 10
  • https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Alex Blex Sep 05 '17 at 16:15
  • Note that the `then` callback is only executed once, so I don't really understand why you have `dataArray`, as you will only push twice to it. – trincot Sep 05 '17 at 16:19
  • Similarly, you cannot resolve a promise twice. The second call to `resolve` will be without effect. – trincot Sep 05 '17 at 16:42
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! – Bergi Sep 05 '17 at 16:45

1 Answers1

1

First of all, avoid the Promise constructor antipattern and use return values instead! For multiple layers of asynchronous functions, just use multiple layers of promises. To await multiple promises at once, use Promise.all:

function getData(){
    return Promise.all([
        getCollection1(),
        getCollection2(),
        getCollection3()
    ]).then(([rows1, rows2, rows3]) => {
        return [someDataProcessingFunction(rows1, rows2)].concat(rows3);
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you very much. I didn't know about Promise.all. But after 1 day studying, I still don't think it can solve my problem because I have multi parallel branches and some branches have sub branches, all may produce part of the result data. I don't know when and how to return in .then function. Promise.all only solve the first parallel layers – vdonkey Sep 07 '17 at 08:57
  • After that I found another solution: ES7 async/await. I can write "real" sync functions with async/await. life is easier with that. I downloaded NodeJS 8.4.0 to use the new async/await on my Ubuntu 16.04. It worked and my code is shortened much – vdonkey Sep 07 '17 at 09:01
  • You can nest these arbitrarily. Make a promise for each sub-result. Call `Promise.all` on the array of subresults. Do this subcalculation inside a `.then()`, which returns a promise again. [Always `return` from every function](https://stackoverflow.com/a/25756564/1048572). – Bergi Sep 07 '17 at 09:02