节点 - 等待循环完成?

时间:2016-12-18 19:52:22

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

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

目前它在函数完成之前发布了[]并且我知道这是因为异步执行,但我认为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
}

2 个答案:

答案 0 :(得分:9)

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

以下是如何做到这一点:

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会提供自己的专辑子集会更好,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
    });
}

答案 1 :(得分:0)

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

我继续编写了一个完整的,可运行的示例,因此您可以将其分解以查看其工作原理。重要的是添加一个计数器,在每个循环后递增计数器,然后检查计数器何时与您重复的列表长度相同。

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');