【问题标题】:Node - Wait for loop to finish?节点 - 等待循环完成?
【发布时间】:2017-05-03 21:27:58
【问题描述】:

当下面的函数完成并提供数组“专辑”中的最终项目列表时,我希望它调用另一个函数/对列表执行其他操作。

目前它在函数完成之前发布 [],我知道这是因为异步执行,但我认为 Node 是线性读取的,因为它是单线程的?

function getAlbumsTotal(list, params){
    for(var i = 0; i<list.length; i++){
        api.getArtistAlbums(list[i], params).then(function(data) {
            for(var alb = 0; alb<data.body.items.length; alb++){
                albums.push(data.body.items[alb].id);
            }
        }, function(err) {
            console.error(err);
        });
    }
    console.log(albums);
    //do something with the finalized list of albums here
}

【问题讨论】:

    标签: javascript node.js for-loop asynchronous callback


    【解决方案1】:

    您提供给then 的回调函数确实是异步执行的,这意味着它仅在当前调用堆栈中的其余代码执行完毕后执行,包括您最终的console.log

    你可以这样做:

    function getAlbumsTotal(list, params){
        var promises = list.map(function (item) { // return array of promises
            // return the promise:
            return api.getArtistAlbums(item, params)
                .then(function(data) {
                    for(var alb = 0; alb<data.body.items.length; alb++){
                        albums.push(data.body.items[alb].id);
                    }
                }, function(err) {
                    console.error(err);
                });
        });
        Promise.all(promises).then(function () {
            console.log(albums);
            //do something with the finalized list of albums here
        });
    }
    

    注意:显然albums 被定义为全局变量。这不是一个很好的设计。最好每个承诺都提供自己的专辑子集,并使用 Promise.all 调用将这些结果连接到局部变量中。下面是它的样子:

    function getAlbumsTotal(list, params){
        var promises = list.map(function (item) { // return array of promises
            // return the promise:
            return api.getArtistAlbums(item, params)
                .then(function(data) {
                    // return the array of album IDs:
                    return Array.from(data.body.items, function (alb) {
                        return alb.id;
                    });
                }, function(err) {
                    console.error(err);
                });
        });
        Promise.all(promises).then(function (albums) { // albums is 2D array
            albums = [].concat.apply([], albums); // flatten the array
            console.log(albums);
            //do something with the finalized list of albums here
        });
    }
    

    【讨论】:

    • 这是最优雅的方式吗?如果我不使用 .then 而是使用类似的东西 function getAlbumsTotal(list, params){ for(var i = 0; i&lt;list.length; i++){ api.getArtistAlbums(list[i], params, function(err, data){ if(!err){ for(var alb = 0; alb&lt;data.body.items.length; alb++){ albums.push(data.body.items[alb].id); } } }); } } 会是相同的解决方案吗?编辑:我猜它没有添加空格,而是 .then,它是函数的函数。
    • 异步性仍然存在:您仍然有一个回调函数,该函数在当前运行的任何其他内容之后执行。所以事实上,你最好使用 Promise,因为像 Promise.all 这样的东西将它们结合在一起。否则,您将不得不在回调的最后一次调用时开始计数。
    • 我用一个与局部变量 albums 而不是全局变量一起工作的版本扩展了我的答案,这将是更好的设计(取决于你用它做什么)。
    【解决方案2】:

    如果您想使用从 node.js 中的循环返回的数据,您必须添加一些额外的代码来检查您是否处于循环的最后一次迭代中。基本上,您正在编写自己的“循环完成”检查并仅在该条件为真时运行。

    我继续编写了一个完整的、可运行的示例,以便您可以分解它以了解它是如何工作的。重要的部分是添加一个计数器,在每次循环后将其递增,然后检查计数器何时与您正在迭代的列表的长度相同。

    function getArtistAlbums(artist, params){
      var artistAlbums = {
        'Aphex Twin':['Syro', 'Drukqs'],
        'Metallica':['Kill \'Em All', 'Reload']
      };
      return new Promise(function (fulfill, reject){
        fulfill(artistAlbums[artist]);
      });
    
    }
    function getAlbumsTotal(list, params){
      var listCount = 0;
      for(var i = 0; i<list.length; i++){
        getArtistAlbums(list[i], params)
          .then(function(data) {
            listCount++;
            for(var alb = 0; alb<data.length; alb++){
            //for(var alb = 0; alb<data.items.length; alb++){
              //albums.push(data.body.items[alb].id);
              albums.push(data[alb]);
            }
            // print out album list at the end of our loop
            if(listCount == list.length){
              console.log(albums);
            }
    
          }, function(err) {
            console.error(err);
          });
      }
      // prints out too early because of async nature of node.js
      //console.log(albums);
    }
    
    var listOfArtists = ['Aphex Twin', 'Metallica'];
    var albums = [];
    
    getAlbumsTotal(listOfArtists, 'dummy params');
    

    【讨论】:

      猜你喜欢
      • 2021-07-14
      • 2020-04-01
      • 1970-01-01
      • 2012-09-30
      • 2020-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-19
      相关资源
      最近更新 更多