【问题标题】:Node.js/Javascript - nested promises and for loop with when.jsNode.js/Javascript - 嵌套 promise 和 for 循环 with when.js
【发布时间】:2015-07-19 22:06:07
【问题描述】:

我目前正在努力处理 promise 的控制流程(promise newbie!)。

我调用了返回一个数组对象的 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 ]

【问题讨论】:

  • 您神奇地期望外部promise 知道内部代码何时完成,但是您没有在它们之间建立任何联系,因此外部promise 不会等待内部promise 完成。因此,当您尝试在代码的最后一行记录 out 的值时,还没有完成任何内部代码。我不知道when-js,但是您必须创建一个主promise,当您完成所有内部promise 时解决该promise 并从外部.then() 处理程序返回它。这将告诉外部承诺等待所有内部活动完成。
  • 您会得到[undefined, undefined],因为.done 回调中的out 不是填充的out 对象。简单的解决方案是.done(function () {console.log(out); });,它确保记录最初定义的out。但是,请参阅下面的答案。

标签: javascript node.js foreach promise when-js


【解决方案1】:

了解流程控制承诺链传递的数据由以下因素决定,这一点很重要:

  • 链的组成,以及任何内链
  • promise 聚合器,例如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 的结果,这种情况仅发生当所有 [geddit] REDISCLIENT.get() 调用返回的承诺已成功解决。

【讨论】:

  • 太棒了 - 非常感谢详细的细分 - 真的很有帮助,尤其是了解 out 如何通过返回传递。让我很困惑!
  • 是的,一些嵌套的返回值应该在几行之后作为其他函数的参数弹出并不是很明显。即使已经习惯了 Promise,有时我仍然需要非常仔细地检查其他人的代码以了解发生了什么。
【解决方案2】:

forEach 循环完成,但您在内创建的 Promise 没有完成。 when.js 实现了 Promises/A+ 规范,它保证了 Promise 的异步解析回调。这意味着,传递给then() 的回调保证在当前调用堆栈完成执行后被调用。

你需要从你的then-callback 返回一个promise,以便将内部promise 连接到外部promise。更具体地说,你需要一个超越所有内在承诺的承诺。

最简单的方法是使用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)

【讨论】:

  • @David_Aurelio - 非常有帮助,谢谢。它就在那里,但没有用键和值附加到 out obj - 外部获取键,每个 for 循环结果都正确返回值。请参阅有关我修改后尝试的问题的更新 1。
猜你喜欢
  • 1970-01-01
  • 2015-10-23
  • 2023-04-04
  • 2017-06-13
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 2017-08-08
  • 2018-10-01
相关资源
最近更新 更多