2016-09-07 5 views
8

私はPromisesを使って再帰関数を作成しようとしていますが、それを正しく得ることはできません。私は約束を使わずに作業コードを持っていますが、カウンターやグローバル変数などを使用していますので、書き直しを試みて再利用のためのモジュールを作成しようとしています。Javascript Recursive Promise

本質的に、この機能はActive Directoryからユーザーを取得してから、直接レポートとその直接レポートなどを再帰的に検索することになっています。私は機能のバージョンがたくさんプレーした

が、これは現在のものです:

function loadReports(personEmail, list) { 
    return new Promise((resolve, reject) => { 
     getAccessTokenPromise() 
      .then(access_token => { 
       list.push(personEmail); 
       return makeRequest(personEmail, access_token); 
      }).then(result => { 
       if (result.value.length > 0) { 
        Promise.all(result.value.map(person => { 
         loadReports(person.userPrincipalName, list); 
        })).then(resolve()); 
       } else { 
        resolve(); 
       } 
      }) 
      .catch(e => reject(e)); 
    }); 
} 

getAccessTokenPromise機能は、アクセストークンの要求を実行し、そのための約束を返します。 makeRequest関数は、ユーザーとそのレポートに対してhttps要求を行い、その結果がPromiseであるjsonオブジェクトを返します。

どのような考えが大きく寄せられました。どうもありがとう。 D.

+1

"しかし、カウンターやグローバル変数などを使用しています" ---今は、不純な関数と自由変数がどのように悪いのかを見ています。最初にそれを再実装して、外側のスコープからの変数に依存しないようにしてから、それを約束します。 – zerkms

答えて

6

約束事で再帰を行うには、通常、すべての再帰的約束を呼び出し元に連鎖したいと思う。これを行うには、.then()ハンドラから約束を元に戻して、元のものにチェーンする必要があります。これはまた、問題を伴う手動で作成された約束で既存の約束をラップすることをあなたのpromise anti-patternを排除する傾向があります。ここではそれを行う一つの方法です:作ら

function loadReports(personEmail, list) { 
    return getAccessTokenPromise().then(access_token => { 
     list.push(personEmail); 
     return makeRequest(personEmail, access_token); 
    }).then(result => { 
     if (result.value.length > 0) { 
      return Promise.all(result.value.map(person => { 
       return loadReports(person.userPrincipalName, list); 
      })); 
     } 
    }); 
} 

変更:

  1. は(getAccessTokenPromise前returnを追加します)ので、我々は最初の約束を返します。これにより、new Promise()と、アンチパターンに関連するすべての手動の​​拒否と解決を排除することもできます。

  2. loadReports()の前にreturnを追加します。これはPromise.all()に渡す前に.map()がその約束を収集できるようにするために行われなければなりません。

  3. Promise.all()の前にreturnを追加すると、元の約束に連鎖されます。


あなたは、データベースのデータにおける真円度の任意の並べ替え(レポートの循環リストを作成し、DB内のエラー)を取得することはできないことを確認する必要があります。 B、BへのレポートはCに、CはAにレポートします。これをお持ちでしたら、このコードは永遠に完了しないでしょう(おそらく最終的にシステムリソースを使い果たしてしまうでしょう)。

これが私のコードだった場合、以前に訪れた人にloadReports()を呼び出すことを拒否して、データベースにアクセスしたすべての人のセットを作成することがあります。これは循環性から安全になります。また、データベースエラー/破損の可能性があるため、この状態がわかった場合は、おそらくlog()にすることをお勧めします。

+0

ありがとうございます。はい、それははるかに良く、はるかに整頓です。説明してくれてありがとう、非常に便利です。最後の '.then(resolve());'を '.then(results => {resolve()})に変更することで、 'しかし、私はあなたのソリューションがはるかに良いのが好きです。もう一度ありがとう。 – Darren

+0

また、最後のビットもありがとうございます。試してみる前に、 "code/changes made"セクションを過ぎていませんでした。興味深いのは、結果が他のシステムのメンバーシップと比較されているので、私は最終的にセットが必要なので、それを紹介するかもしれません。情報はhttp://graph.microsoft.ioから来ているので、うまくいけば信頼できる;)。 – Darren

+0

@Darrenの場合、 '.then(resolve)'だけを考慮する必要があります。 :) –