web-dev-qa-db-ja.com

配列のforEachループでpromiseを使用してオブジェクトを設定する方法

配列でforEachループを実行し、promiseを返す2つの呼び出しを行っています。this.optionsなどのオブジェクトにデータを入力し、それを使って他の処理を行います。現在、次のコードサンプルを使用して、最初にthen関数を取得すると、非同期の問題が発生します。

$.when.apply($, someArray.map(function(item) {
    return $.ajax({...}).then(function(data){...});
})).then(function() {
    // all ajax calls done now
});

これは以下の作業コードですが、応答の.thenで結果の関数を呼び出すため、配列の最初の要素に対してのみ機能します。配列のすべての要素に対して最初にすべてのフェッチを実行してから、結果の関数を呼び出して何かを実行したいと思います。

array.forEach(function(element) {
    return developer.getResources(element)
        .then((data) = > {
            name = data.items[0];
            return developer.getResourceContent(element, file);
        })
        .then((response) = > {
            fileContent = atob(response.content);
            self.files.Push({
                fileName: fileName,
                fileType: fileType,
                content: fileContent
            });
            self.resultingFunction(self.files)
        }).catch ((error) = > {
            console.log('Error: ', error);
        })
});

ForEachループが完了した後、self.filesオブジェクトに値を設定し、ファイルオブジェクトで結果の関数を呼び出すにはどうすればよいですか?

21
rond

Promise.all()はここで役立ちます:

var promises = [];

array.forEach(function(element) {
    promises.Push(
        developer.getResources(element)
            .then((data) = > {
                name = data.items[0];
                return developer.getResourceContent(element, file);
            })
            .then((response) = > {
                fileContent = atob(response.content);
                self.files.Push({
                    fileName: fileName,
                    fileType: fileType,
                    content: fileContent
                });
            }).catch ((error) = > {
                console.log('Error: ', error);
            })
    );
});

Promise.all(promises).then(() => 
    self.resultingFunction(self.files)
);

これにより、各アイテムのAJAX呼び出しが開始され、呼び出しが完了するとself.filesに各呼び出しの結果が追加され、すべての呼び出しが完了した後にself.resultingFunction()が呼び出されます。

編集: Yury Tarabankoの提案に基づいて簡略化。

59
Timo

上記の承認されたソリューションのわずかなバリエーションは次のとおりです。

var promises = array.map(function(element) {
      return developer.getResources(element)
          .then((data) = > {
              name = data.items[0];
              return developer.getResourceContent(element, file);
          })
          .then((response) = > {
              fileContent = atob(response.content);
              self.files.Push({
                  fileName: fileName,
                  fileType: fileType,
                  content: fileContent
              });
          }).catch ((error) = > {
              console.log('Error: ', error);
          })
});

Promise.all(promises).then(() => 
    self.resultingFunction(self.files)
);

Array.mapの代わりにArray.forEachを使用しました。つまり、最初に空の配列を作成する必要はなく、既存の配列を再利用するだけです。

21
IonicBurger

優れたヒントについては、 同様の質問に対するこの回答 をご覧ください。そこで提供されているソリューションでは、Array#reduce()を使用するのではなく、Promise.all()を使用して、作業を行う前にすべてのPromiseを蓄積する必要がありません。

1
Alan Mimms

次のコードは、Promiseを使用した同期の簡単な理解です。

let numArr = [1,2,3,4,5];
let nums=[];

let promiseList = new Promise(function(resolve,reject){
  setTimeout(()=>{
        numArr.forEach((val)=>{
        nums.Push(val);
    });
    resolve(nums);
 },5000)
})


Promise.all([promiseList]).then((arrList)=>{
  arrList.forEach((array)=>{
    console.log("Array return from promiseList object ",array);    
  })

 });

上記の例では、配列numsを5秒間保持します。また、リリース後にコンソールに印刷されます。

0
jasmeetsohal