简体   繁体   中英

Node.js/Javascript - nested promises and for loop with when.js

I am currently struggling with the control flow of promise (promise newbie!).

I make a call to Redis which returns an array object. I then iterate through each of the results and call back to Redis to get a value and wish to populate these in to a final object out .

The out object is never populated, im guessing as the forEach has not completed:

(note the Redis client lib returns a when.js based promise as default)

var out = {};

REDISCLIENT.keys("apikey:*")
    .then(function (replies) {

        replies.forEach(function (key, index) {

            REDISCLIENT.get(key)
                .then(function (value) {
                    out[key] = value;
                })
                .done(console.log(out));

        });

    })
    .catch(console.log)
    .done(console.log(out));

How can I guarantee that the forEach loop is complete?

I have read many similar posts (I know this is a duplicate) however have not been able to understand why the inner done() method does not contain the fully complete out obj.

Im guessing I need to wrap the forEach itself in a promise? Appreciate any direction.


Update 1: huge thanks to @David_Aurelio. I now need to populate out with the key and values. Here is my attempt:

GLOBAL.REDISCLIENT.keys("apikey:*")
        .then(function (replies) {
            return when.all(replies.map(function (key, index) {
                return GLOBAL.REDISCLIENT.get(key)
                    .then(function (val) {
                        out[key] = val;
                        console.log(key, val);
                    });
            }));
        })
        .catch(console.log)
        .done(function (out) {console.log(out); });

The inner console.log prints the correct key/values

key1 val1
key2 val2

The final done now prints:

[ undefined, undefined ]

The forEach loop completes, but the Promises you create inside don't. when.js implements the Promises/A+ spec, which guarantees asynchronous resolution of promise callbacks. That means, that the callback passed to then() is guaranteed to be invoked after the current call stack has finished to execute.

You need to return a promise from your then -callback in order to connect the inner promises to the outer promise. More specifically, you need a promise over all inner promises.

The easiest way to do that is to use when.all :

REDISCLIENT.keys("apikey:*")
  .then(function (replies) {
    return when.all(replies.map(function (key, index) {
      return REDISCLIENT.get(key);
    }));
  })
  .catch(console.log)
  .done(function (out) {console.log(out); });

In your original code, you also don't register a callback to done , but call console.log(out) immediately, before the first promise has even resolved.

It's important to understand that flow control and the data conveyed by a promise chain, are determined by :

  • the composition of the chain, and any inner chain(s)
  • promise aggregators, such as when.all()
  • return statements

Here's how to achieve what you want with out as an inner member.

REDISCLIENT.keys("apikey:*")
.then(function (replies) {
    var out = {}: //<<<<< initiate `out` as an inner member
    return when.all(replies.map(function (key, index) { //<<<<< here's David Aurelio's when.all(replies.map(...))
        return REDISCLIENT.get(key).then(function (value) { //<<<<< `return` here causes `.map()` to build an array of promises.
            out[key] = value;
        });
    })).then(function() { //<<<< here's an additional `.then()` chained to `when.all(...)`
        return out; //<<<<< `return` here makes the populated `out` available to the `.done()` callback below.
    });
})
.catch(console.log)
.done(function (out_) {
    console.log(out_);
});

The ugly outer member has disappeared!

In the .done() callback, I have changed the member name to out_ in order to emphasize that it is passed as a consequence of that return out , which happens only when all [geddit] the promises returned by REDISCLIENT.get() calls have successfully settled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM