2017-01-19 8 views
0

多くの非同期操作を含むメソッドへの再帰呼び出しを処理する際に問題が発生する以下のプログラムがあります(実行可能にする必要があります)。Promise.all recursive

プログラムの出発点として、私はtest()メソッドを持っています。それはrootオブジェクト(ネストされた "要素"のツリー)を渡す_add()への最初の呼び出しをトリガします。

メソッドは、storage(ここでは単純な配列で表しています)に入れ子要素を追加します。これを行うために、私はすべての子要素でそれ自身を呼び出すように関数を記述しました。

要素の格納は非同期操作です。したがって、私は、すべての子要素の「まだ利用できない」値を保持するという約束を使用しています。

test()で示されているように、ここでは同期を正しく処理できません。 add()の最初の呼び出しによって返された約束が解決される前に、すべての要素がストレージに挿入されているはずです。これは、プログラムの出力が示すように、明らかにそうではありません。

もちろん、子のparentIdはその親のidを参照するので、格納操作は要素のツリーが探索されるのと同じ順序で行われることが重要です。

storageから要素を取得しようとしたときに、まったく同じ種類の問題が発生することが予想されます。

ご協力いただければ幸いです。

ありがとうございます。

(() => { 
    const storage = []; 
    const root = { 
    _type: "root", 
    title: "root element", 
    gravity: null, 
    children: [ 
     { 
     _type: "space", 
     title: "my first space", 
     gravity: 0, 
     children: [ 
      { 
      _type: "bundle", 
      title: null, 
      gravity: 0, 
      children: [ 
       { 
       _type: "vis", 
       title: "my first vis", 
       gravity: 0, 
       visualization: { 
        url: "http://dummyvis.com/#8766" 
       }, 
       children: null 
       }, 
       { 
       _type: "feature", 
       title: "my feature 1", 
       gravity: 1, 
       feature: { 
        componentType: "CPT_FEATURE1" 
       }, 
       children: [] 
       }, 
       { 
       _type: "feature", 
       title: "my feature 2", 
       gravity: 1, 
       feature: { 
        componentType: "CPT_FEATURE2" 
       }, 
       children: [] 
       } 
      ] 
      } 
     ] 
     } 
    ] 
    }; 

    const store = (element, parentId) => { 
    return new Promise((resolve, reject) => { 

     storage.push({id:element.id, _type:element._type, gravity: element.gravity, parent:parentId, created_date:Date.now() }); 

     setTimeout(() => resolve(element), 500); 
    }); 
    } 

    const _add = (element, recurse, parentId) => { 
    console.log(element._type); 
    if(!element._type) 
     throw new Error("an element must declare a key '_type'"); 
    parentId = parentId || null; 

    return new Promise((resolve, reject) => { 
     const promises = []; 

     promises.push(new Promise((resolve, reject) => { 
     store(element, parentId) 
      .then(element => { 
      resolve(element); 
      }) 
      .catch(err => reject(err)); 
     })); 

     Promise.all(promises) 
     .then(res => { 
      if(recurse && element.children.length > 0) { 
      element.children.forEach(child => { 
       _add(child, recurse, element.id); 
      }); 
      } 
      resolve(element); 
     }) 
     .catch(err => reject(err)); 
    }); 
    } 

    const test =() => { 
    _add(root, true) 
     .then(res => { 
     console.log("----------------------------------------------------------"); 
     console.log("Test completed. Nothing should be printed after this line!"); 
     console.log("------------------------------------------------------...-"); 
     }) 
     .catch(err => { 

     }); 
    } 
    test(); 
})(); 

出力:

decisionspace 
---------------------------------------------------------- 
Test completed. Nothing should be printed after this line! 
------------------------------------------------------...- 
bundle 
visualization 
feature 
feature 
+0

は[ 'Promise'コンストラクタアンチパターン](http://stackoverflow.com/q/23803743/1048572?What-is-the-promise-constructionを避けてくださいそれは避けてください!) – Bergi

答えて

0

問題はここにある:

​​3210

_add()はすぐに約束を返し、非同期的に、後でそのタスクを完了します。 forEachループが完了すると、_add()はまだタスクを完了していません。

あなたは要素が以前_add()が完了したことを待って、次のいずれかを呼び出す前に、タスクで、順に追加されていることを確認したい場合は、あなたが書くことができます。

Promise.all(promises) 
    .then(res => { 
     var taskQueue = Promise.resolve(); 
     if(recurse && element.children.length > 0) { 
     element.children.forEach(child => { 
      taskQueue = taskQueue.then(() => _add(child, recurse, element.id)); 
     }); 
     } 
     taskQueue.then(() => resolve(element)); 
    }) 

場合は気にしない場合要素を順に追加されていない、あなたが書くことができます。

Promise.all(promises) 
    .then(res => { 
     var tasks = []; 
     if(recurse && element.children.length > 0) { 
     element.children.forEach(child => { 
      tasks.push(_add(child, recurse, element.id)); 
     }); 
     } 
     Promise.all(tasks).then(() => resolve(element)); 
    })