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 :
when.all()
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.