【问题标题】:How to make a for statement wait multiple mongoose queries?如何让 for 语句等待多个猫鼬查询?
【发布时间】:2016-08-16 12:58:43
【问题描述】:

我目前正在尝试做的是显示所有包含歌曲的播放列表。为此,我首先找到每个播放列表,然后执行 for 循环遍历所有播放列表(同时我初始化 globalArr 并输入值,然后它将作为 json 发送,因为它是一个 API),问题是当我这样做时循环中的另一个查找(PlaylistSong.find 或 Song.find)很好,因为它是异步的,当 for 结束时将进行查找,我将有 0 个结果,因为当他将采用 increment 的值时在他的最大。我听说过async,我什至用谷歌搜索过,但我真的不明白如何执行这段代码,因为它是 for 循环和异步查询的组合......

感谢您的帮助。

router.get('/', function(req, res, next) {
    Playlist.find(function (err, playlists) {
        if (err) return next(err);
        /* Loop through every playlists */
        var globalArr = [];
        for (var increment = 0; increment < playlists.length; ++increment)
        {
            globalArr[increment] = [];
            globalArr[increment]["name"] = playlists[increment].name;
            /* Loop through every links between Songs and Playlist */
            PlaylistSong.find({idPlaylist: playlists[increment]._id}, function (err, songs) {
                if (err) return next(err);
                for (var songIncrement = 0; songIncrement < songs.length; ++songIncrement) {
                {
                    console.log("increment"+increment);
                    globalArr[increment][songIncrement] = [];
                    /* Getting the actual song by his ID */
                    Song.find({_id: song.idSong}, function (err, song) {
                        if (err) return next(err);
                        globalArr[increment][songIncrement]["name"] = songs[songIncrement].name;
                        globalArr[increment][songIncrement]["artist"] = songs[songIncrement].artist;
                        globalArr[increment][songIncrement]["picture"] = songs[songIncrement].picture;
                        globalArr[increment][songIncrement]["price"] = songs[songIncrement].price;
                        globalArr[increment][songIncrement]["file"] = songs[songIncrement].file;
                        globalArr[increment][songIncrement]["difficulty"] = songs[songIncrement].difficulty;
                        globalArr[increment][songIncrement]["downloaded"] = songs[songIncrement].downloaded;
                    });
                }

            }});
        }
        res.contentType('application/json');
        res.send(JSON.stringify(globalArr));
    });
});

【问题讨论】:

    标签: node.js mongodb asynchronous mongoose


    【解决方案1】:

    查看这个问题和接受的答案: Simplest way to wait some asynchronous tasks complete, in Javascript?

    它基本上说要使用Async module,将所有异步函数调用推送到它上面,然后使用async.parallel(),它会在所有异步函数完成后给你一个回调。

    我还没有测试过,但这样的东西似乎可以工作:

    var async = require('async');
    
    var calls = [];
    
    router.get('/', function(req, res, next) {
        Playlist.find(function (err, playlists) {
            if (err) return next(err);
            /* Loop through every playlists */
            var globalArr = [];
            for (var increment = 0; increment < playlists.length; ++increment)
            {
                (function() {
                    var i = increment;
                    calls.push(function(callback) {
                        globalArr[i] = [];
                        globalArr[i]["name"] = playlists[i].name;
                        /* Loop through every links between Songs and Playlist */
                        PlaylistSong.find({idPlaylist: playlists[increment]._id}, function (err, songs) {
                            if (err) return next(err);
                            for (var songIncrement = 0; songIncrement < songs.length; ++songIncrement) {
                            {
                                console.log("increment"+i);
                                globalArr[i][songIncrement] = [];
                                /* Getting the actual song by his ID */
                                Song.find({_id: song.idSong}, function (err, song) {
                                    if (err) return next(err);
                                    globalArr[i][songIncrement]["name"] = songs[songIncrement].name;
                                    globalArr[i][songIncrement]["artist"] = songs[songIncrement].artist;
                                    globalArr[i][songIncrement]["picture"] = songs[songIncrement].picture;
                                    globalArr[i][songIncrement]["price"] = songs[songIncrement].price;
                                    globalArr[i][songIncrement]["file"] = songs[songIncrement].file;
                                    globalArr[i][songIncrement]["difficulty"] = songs[songIncrement].difficulty;
                                    globalArr[i][songIncrement]["downloaded"] = songs[songIncrement].downloaded;
                                });
                            }
                            callback();
                        }});
                    });
                })();
            }
            async.parallel(calls, function(err, result) {
                if (err) {
                    // TODO: Handle error here
                }
                res.contentType('application/json');
                res.send(JSON.stringify(globalArr));
            });
        });
    });
    

    或者如果您不希望 then 并行执行,您可以改用 async.series()

    有关您的情况的简化示例,请参阅此 jsFiddle...https://jsfiddle.net/bpursley/fj22hf6g/

    【讨论】:

    • 仍然是同样的问题,当函数被调用时 increment = 2 而它必须是 0 然后 1(我有 2 个播放列表)
    • 我认为这是因为循环在函数执行之前完成(延迟执行)并且增量已经完成。尝试将增量移动到局部变量中并改用该变量。我将用我正在谈论的内容编辑我的答案。
    • 好的,我更新了代码示例,还包含了一个 jsFiddle,显示了我正在做的事情。诀窍是通过使用 IIFI 并将迭代放入局部变量 i 来获得一个新的范围,这样下一次迭代就不会改变它。我也忘了把回调放在我之前的例子中(我说我没有运行它)。但是检查我的 jsfiddle,我想你会看到一个简单的例子来说明我正在做的事情。我不认为这是解决问题的唯一方法,但它是解决问题的方法。
    • 由于您有多个嵌套循环,您可能必须在内部和外部循环中都这样做。我实际上会考虑将这段代码分解成函数来简化它。我上面的代码示例不完整,如果不进行一些修改,可能无法为您工作。老实说,我不太确定为什么你必须有两个循环,一个用于增量,另一个用于 songIncrement,但你比我更了解你的场景。不过看看我的 jsFiddle,你就会明白我的建议...jsfiddle.net/bpursley/fj22hf6g
    【解决方案2】:

    是的,你应该使用异步。稍后我会更详细地解释这一点(儿子必须去睡觉......)

    PlaylistSong.statics.findSongsByPlaylistId = function(id, done) {
      PlaylistSong.find({idPlaylist: id}, function(err, songs) {
        if (err) {
          done(err)
          return
        }
        var getSongsFns = songs.map(function(song) {
          return function(callback) {
            Song.find({_id: song.idSong}, callback)
          }
        })
        async.parallel(getSongsFns, done)
      })
    }
    
    router.get('/', function(req, res, next) {
        Playlist.find(function (err, playlists) {
            if (err) return next(err);
            var getSongsFns = playlists.map(function(playlist) {
              return function(callback) {
                PlaylistSong.findSongsByPlaylistId(playlist._id, callback)
              }
            })
            async.parallel(getSongsFns, function(err, songs) {
              if (err) {
                res.status(500).send()
                return
              }
              res.contentType('application/json');
              res.send(JSON.stringify(songs));
            }) 
        });
    });

    【讨论】:

    • 差不多就是这样,但是播放列表缺少“名称”字段(每个播放列表都有一个名称),并且没有我想要的歌曲的字段名称歌曲:[这里的结果]你看到了吗? [ [ [ { "_id": "56d006faa96d4211007e7e50", "name": "Stressed out", "__v": 0, "updated_at": "2016-02-26T08:04:10.649Z", "created_at": "UDT" } ], [ { "_id": "ID", "name": "Test", "__v": 0, "updated_at": "CRT", "created_at": "UDT" } ] ], [] ]
    猜你喜欢
    • 2016-11-19
    • 2014-03-03
    • 2018-08-09
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    • 2020-07-19
    • 2018-11-29
    • 1970-01-01
    相关资源
    最近更新 更多