简体   繁体   English

节点 - 等待循环完成?

[英]Node - Wait for loop to finish?

When the function below finishes and provides a finalized list of items in the array 'albums', I want it to call another function/do something else with the list. 当下面的函数完成并提供数组'albums'中的项目的最终列表时,我希望它调用另一个函数/对列表执行其他操作。

Currently it posts [] before the function finishes and I know that's because of asynchronous execution, but I thought Node read linearly since it's single threaded? 目前它在函数完成之前发布[]并且我知道这是因为异步执行,但我认为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
}

The callback function you provide to then is indeed executed asynchronously, which means it is only executed after the rest of the code in the current call stack has finished executing, including your final console.log . 您提供给then的回调函数确实是异步执行的,这意味着它只在当前调用堆栈中的其余代码执行完毕后执行,包括最终的console.log

Here is how you could do it: 您可以这样做:

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

NB: Apparently albums is defined as a global variable. 注意:显然, albums被定义为全局变量。 This is not so good a design. 这不是一个好设计。 It would be better that each promise would provide its own subset of albums, and the Promise.all call would be used to concatenate those results into a local variable. 每个promise会提供自己的专辑子Promise.all更好,并且Promise.all调用将用于将这些结果连接到局部变量中。 Here is how that would look like: 这是如何看起来像:

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

If you want to use data returned from a loop in node.js you have to add a little extra code to check if you're on the last iteration of the loop. 如果你想使用从node.js中的循环返回的数据,你必须添加一些额外的代码来检查你是否在循环的最后一次迭代。 Basically you're writing your own "loop complete" check and running only when that condition is true. 基本上,您正在编写自己的“循环完成”检查并仅在该条件为真时运行。

I went ahead and wrote a complete, runnable example so you can break it down to see how it works. 我继续编写了一个完整的,可运行的示例,以便您可以将其分解以查看其工作原理。 The important part is to add a counter, increment it after each loop, then check for when the counter is the same length of the list that you're iterating over. 重要的是添加一个计数器,在每个循环后递增计数器,然后检查计数器何时与您迭代的列表长度相同。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM