forEach over array with .push into another array

时间:2015-05-11 23:40:33

标签: javascript node.js express mongoose

我正在使用Node / Mongoose / Express应用程序,我在使用forEach时遇到更新数组时遇到问题。我不是百分百肯定我错过了什么。这与同步与异步有关吗?我也可能只是错了。

router.route('/:slug/episodes')

.get(function(req, res) {
    var episodeArray = [];
    var episodeDetails = null;
    Show.findOne({ 'slug': req.params.slug }, function(err, show) {
        if (err) {
            res.send(err);
        }
        var episodes = show.episodes
        episodes.forEach(function(episodeID, index) {
            Episode.findById(episodeID, function(err, episode) {
                if (err) {
                    res.send(err);
                }
                episodeArray.push(episode);
            });
        });
        res.send(episodeArray)
    });
});

episodeArray没有添加剧集,最终只是充满了空值。

2 个答案:

答案 0 :(得分:0)

在将数组发送回客户端之前,您不是在等待异步操作完成。

尝试这样的事情:

    var togo = episodes.length;
    var error;
    episodes.forEach(function(episodeID, index) {
      Episode.findById(episodeID, function(err, episode) {
        if (err) {
          if (!error)
            error = err;
        } else
          episodeArray.push(episode);
        if (--togo === 0)
          res.send(error ? error : episodeArray);
      });
    });

此外,如果在异步操作期间遇到错误,您应该添加return;,以防止执行其余代码。

答案 1 :(得分:0)

您的代码误解了异步的工作方式。首先,你应该阅读@elclanrs发布的Felix Kling链接 here。 SO贡献者往往厌倦了一遍又一遍地回答相同的异步问题。我还没有那么累,所以我会为了解释异步而咬人,但我还会建议另一种选择,这可能是解决问题的更好方法。

异步解决方案:有许多方法可以等待异步操作数组完成。 async.queue是一个受欢迎的选择。工作原理是将一组待处理操作推送到队列中,然后告诉该队列等待直到收到所有结果,此时执行res.send()。代码看起来像这样:

var async = require('async');

Show.findOne({
    'slug': req.params.slug
}, function(err, show) {
        if (err) {
            res.send(err);
        }

        var episodeArray = [];
        var queue = async.queue(function(episodeID, callback) {
            Episode.findById(episodeID, function(err, episode) {
                if (err) {
                    throw err;
                }
                episodeArray.push(episode);
                callback();
            });
        });

        // Note that forEach is synchronous.
        // The tasks will be pushed to the queue before drain()
        episodes.forEach(function(episodeID, index) {
            queue.push(episodeId);
        });

        queue.drain = function() {
            res.send(episodeArray);
        };
    });

这不是解决问题的最佳方式,这只是为了演示如何修复现有代码。

只要您的剧集阵列不是非常庞大,那么查询剧集的更好方法可能是使用mongoDB的$in运算符,如下所示:

Show.findOne({
    'slug': req.params.slug
}, function(err, show) {
        if (err) {
            res.send(err);
        }

        Episode.find({
            _id: {
                $in: show.episodes
            }
        }, function(err, episodes) {
                if (err) {
                    throw err;
                }
                res.send(episodes);
            });
    });

修改

如果你想深入一点,我还应该提一下,mongoose支持promises,你可以使用它来使你的代码更少嵌套和重复,如下所示:

Show.findOne({
    slug: req.params.slug
})
.then(function(show) {
    return Episode.find({
        _id: {
            $in: show.episodes
        }
    });
})
.then(function(episodes) {
    res.send(episodes);
})
// Any errors returned above will short circuit to the middleware next() here
.error(next);