web-dev-qa-db-ja.com

Node-ループが終了するのを待ちますか?

以下の関数が終了し、配列 '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
}
7
Ralph

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

Node.jsのループから返されたデータを使用する場合は、ループの最後の反復であるかどうかを確認するために、少し余分なコードを追加する必要があります。基本的に、独自の「ループ完了」チェックを記述し、その条件がtrueの場合にのみ実行します。

私は先に進んで、実行可能な完全な例を書いたので、それを分解してどのように機能するかを確認できます。重要な部分は、カウンターを追加し、各ループの後にインクリメントし、カウンターが繰り返しているリストと同じ長さであるかどうかを確認することです。

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');
0
Jim Factor