forEach over array with .push into another array

forEach over array with .push into another array

我正在开发 Node / Mongoose / Express 应用程序,但在使用 forEach 时遇到要更新数组的问题。我不是 100% 确定我错过了什么。这与同步与异步有关吗?我也可能只是 return 错了。

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 没有添加剧集,最终只是充满了空值。

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

尝试这样的事情:

    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;,以防止执行其余代码。

您的代码对异步的工作方式存在误解。首先,您应该阅读@elclanrs 发布的 Felix Kling link 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);