Node.js/Javascript - вложенные обещания и цикл for с when.js
В настоящее время я борюсь с потоком управления обещанием (обещаю новичку!).
Я делаю вызов Redis, который возвращает объект массива. Затем я перебираю каждый из результатов и перезваниваю в Redis, чтобы получить значение, и хочу заполнить их конечным объектом. out
,
Объект out никогда не заполняется, я предполагаю, что forEach еще не завершен:
(обратите внимание, что библиотека клиента Redis по умолчанию возвращает обещание на основе when.js)
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));
Как я могу гарантировать, что цикл forEach завершен?
Я прочитал много похожих постов (я знаю, что это дубликат), однако не смог понять, почему внутренний метод done() не содержит полностью завершенного out
OBJ.
Я предполагаю, что мне нужно обернуть forEach в обещание? Цените любое направление.
Обновление 1: огромное спасибо @David_Aurelio. Теперь мне нужно заселить out
с ключом и ценностями. Вот моя попытка:
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); });
Внутренний console.log печатает правильный ключ / значения
key1 val1
key2 val2
Финал готово теперь печатает:
[ undefined, undefined ]
2 ответа
Важно понимать, что управление потоком и данные, передаваемые цепочкой обещаний, определяются:
- состав цепи и любая внутренняя цепь (и)
- агрегаторы обещаний, такие как
when.all()
- возврат заявления
Вот как добиться того, что вы хотите с out
как внутренний член.
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_);
});
Уродливый внешний член исчез!
в .done()
обратный вызов, я изменил имя участника на out_
чтобы подчеркнуть, что это передается как следствие этого return out
, что происходит только тогда, когда все обещания возвращаются REDISCLIENT.get()
звонки успешно урегулированы.
forEach
цикл завершается, но обещания, которые вы создаете внутри , не выполняются. когда.js реализует спецификацию Promises/A+, которая гарантирует асинхронное разрешение обратных вызовов обещаний. Это означает, что обратный вызов передан then()
гарантированно будет вызван после завершения текущего стека вызовов.
Вам нужно вернуть обещание от вашего then
Обратный звонок, чтобы связать внутренние обещания с внешним обещанием. Точнее, вам нужно обещание над всеми внутренними обещаниями.
Самый простой способ сделать это - использовать 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); });
В исходном коде вы также не регистрируете обратный вызов done
, но позвони console.log(out)
немедленно, еще до того, как первое обещание было решено.