2016-11-23 12 views
6

私はそれ以外の場合には結果配列を返すか返す作業があれば約束を返す非同期再帰関数を持っています。再帰が含まれていない場合は正しく配列を返しますが、再帰がある場合は配列は未定義です。コードは、私は結果の配列を取得し、javascriptは再帰を約束します

function foo(filepath) { 

    var resultArr = []; 

    function doo(file) { 
     return asyncOperation(file).then(resp => { 
      resultArr.push(resp.data); 
      if (resp.pages) { 
       var pages = resp.pages.split(','); 
       pages.forEach(page => { 
        return doo(page); 
       }); 
      } else { 
       return resultArr; 
      } 
     }); 
    } 

    return doo(filepath); 
} 

そして、私は何のresp.pagesを持たないabcfileを渡すと、これは

foo(abcfile).then(function(result){ 
    console.log(result); 
}); 

と呼ばれる方法ですが、resp.pagesは、その後、結果の配列があります定義されていません。

+0

'pages'データで何をしたいですか?あなたは 'if(resp.pages)'ブロックに何も返さず、 'forEach'に渡された関数で使われている戻り値は何もしません。 – Phil

+1

' resp.pages'が真の場合、何も返しません。 'return undefined'を返します。 – Leo

答えて

-1

ページがあるときに何も返されないという問題があります。

function foo(filepath) { 

    var resultArr = []; 

    function doo(file, promises) { 
     let promise = asyncOperation(file).then(resp => { 
      resultArr.push(resp.data); 
      if (resp.pages) { 
       var pages = resp.pages.split(','); 
       var pagePromises = []; 
       pages.forEach(page => { 
        return doo(page, pagePromises); 
       }); 

       //Return the pages 
       return pagePromises; 
      } else { 
       return resultArr; 
      } 
     }); 

     //They want it added 
     if (promises) { promises.push(promise); } 

     //Just return normally 
     else { return promise; } 
    } 

    return doo(filepath); 
} 
+0

これは再帰非同期操作を待つことはありません – Phil

+0

@Phil私はあなたが今したいことを行うように更新しました。 –

3

私はあなただけで私はので、多分試みるdoo関数の外resultArrをスコープに問題がある可能性があり思っている

if (resp.pages) { 
    return Promise.all(resp.pages.split(',').map(page => doo(page))) 
     .then(pagesArr => { 
      return resultArr.concat(...pagesArr) 
     }) 
} 

if (resp.pages)ブロック内で返された約束が欠けていると思いますこの

function foo(filepath) { 
    function doo(file) { 
     return asyncOperation(file).then(resp => { 
      const resultArr = [ resp.data ] 
      if (resp.pages) { 
       return Promise.all(resp.pages.split(',').map(page => doo(page))) 
        .then(pagesArr => resultArr.concat(...pagesArr)) 
      } else { 
       return resultArr 
      } 
     }) 
    } 

    return doo(filePath) 
} 

は、ファイルtopのための3つのページを持っていると言う...それをこのように見て、拡散演算子の使用を説明します。 page1page2page3とサブページそれぞれのカップルと、それらの解決のそれぞれあなたなしconcatを使用する場合は、pagesArr

[ 
    ['page1', 'page1a', 'page1b'], 
    ['page2', 'page2a', 'page2b'], 
    ['page3', 'page3a', 'page3b'] 
] 

resultArr次のようになり、これまで

['top'] 

のように見えますスプレッド演算子を使用すると、最終的には

となります。
[ 
    "top", 
    [ 
    "page1", 
    "page1a", 
    "page1b" 
    ], 
    [ 
    "page2", 
    "page2a", 
    "page2b" 
    ], 
    [ 
    "page3", 
    "page3a", 
    "page3b" 
    ] 
] 

ただし、sp読んで、あなたはここでの問題はif (resp.pages)ブランチで非同期/同期操作を混合さ

[ 
    "top", 
    "page1", 
    "page1a", 
    "page1b", 
    "page2", 
    "page2a", 
    "page2b", 
    "page3", 
    "page3a", 
    "page3b" 
] 
+0

私はフラットリストを作成するためにコンカットを使用する必要があります。 – ams

+0

@Phil配列の範囲が約束と一緒に問題になるかもしれないと思います – ams

+0

@amsだから私は編集で移動しました – Phil

0

を取得します。約束事が期待どおりに働くようにするには、基本的にコールバックから約束を返さなければなりません。フィルの答えに加えて

、あなたが順番/シーケンスでページを実行したい場合は

if (resp.pages) { 
    var pages = resp.pages.split(','); 
    return pages.reduce(function(p, page) { 
    return p.then(function() { 
     return doo(page); 
    }); 
    }, Promise.resolve()); 
} 
+0

OPの 'asyncOperation'が複数のリクエストを同時に実行できないのに、 way – Phil

1

これは私がfakeデータセットを作ってあげる、作品、および非同期データセットからデータを読み込み、fakeAsyncOperation確認するには。データを密接にモデリングするために、偽のデータセットからの各クエリは、datapagesフィールドの応答を返します。

let fake = new Map([ 
    ['root', {data: 'root', pages: ['a', 'b', 'c', 'd']}], 
    ['a',  {data: 'a',  pages: ['a/a', 'a/a']}], 
    ['a/a',  {data: 'a/a',  pages: []}], 
    ['a/b',  {data: 'a/b',  pages: ['a/b/a']}], 
    ['a/b/a', {data: 'a/b/a', pages: []}], 
    ['b',  {data: 'b',  pages: ['b/a']}], 
    ['b/a',  {data: 'b/a',  pages: ['b/a/a']}], 
    ['b/a/a', {data: 'b/a/a', pages: ['b/a/a/a']}], 
    ['b/a/a/a', {data: 'b/a/a/a', pages: []}], 
    ['c',  {data: 'c',  pages: ['c/a', 'c/b', 'c/c', 'c/d']}], 
    ['c/a',  {data: 'c/a',  pages: []}], 
    ['c/b',  {data: 'c/b',  pages: []}], 
    ['c/c',  {data: 'c/c',  pages: []}], 
    ['c/d',  {data: 'c/d',  pages: []}], 
    ['d',  {data: 'd',  pages: []}] 
]); 

let fakeAsyncOperation = (page) => { 
    return new Promise(resolve => { 
    setTimeout(resolve, 100, fake.get(page)) 
    }) 
} 

次に、お客様のfoo機能があります。 dooenqueueに変更しました。キューのように機能するためです。これには、累積データを追跡するためのaccと、キュー内の項目であるxs(非構造化)の2つのパラメータがあります。

私は新しいasync/awaitという構文を使用しました。これは、これに特に対処するのに最適です。プロミスを手作業で作成したり、マニュアルの.thenチェーンを扱う必要はありません。あなたがより多くのことを好きなら、私の読みやすさは、しかし、あなたは簡単にconcat通話acc.concat([data])xs.concat(pages)のためにこれらを置き換える可能性があるため

私は、再帰呼び出しでスプレッド構文のリベラル利用しました。これは関数型プログラミングなので、好きな不変の操作を選んで使います。

最後に、Promise.allを使用する他の回答とは異なり、これは各ページをシリーズに処理します。ページが50個のサブページを持つ場合、Promise.allパラレルで50個のリクエストを作成しようとしますが、これは望ましくない可能性があります。プログラムを並列から直列に変換することは必ずしも単純ではないので、これがこの答えを提供する理由です。

function foo (page) { 
    async function enqueue (acc, [x,...xs]) { 
    if (x === undefined) 
     return acc 
    else { 
     let {data, pages} = await fakeAsyncOperation(x) 
     return enqueue([...acc, data], [...xs, ...pages]) 
    } 
    } 
    return enqueue([], [page]) 
} 

foo('root').then(pages => console.log(pages))

出力

[ 'root', 
    'a', 
    'b', 
    'c', 
    'd', 
    'a/a', 
    'a/a', 
    'b/a', 
    'c/a', 
    'c/b', 
    'c/c', 
    'c/d', 
    'b/a/a', 
    'b/a/a/a' ] 

私は、溶液中のfoo機能があまりにも遠く、元からではないことを幸せ

備考 - 私はあなたがいることに感謝と思います。彼らは両方ともループのための内部補助機能を使用し、同様の方法で問題にアプローチします。 async/awaitはコードをフラットで読みやすく(imo)しています。全体として、これはやや複雑な問題のための優れた解決策だと私は思います。

ああ、約循環参照を忘れないでください。私のデータセットに循環参照はありませんが、ページ'a'pages: ['b']、ページ'b'pages: ['a']の場合、無限再帰が予想されます。この回答はページを連続的に処理するため、これは修正するのが非常に簡単です(累積値accに既存のページ識別子をチェックすることによって)。これは、ページを並行して処理するときに処理するのがはるかに手間がかかります(この回答の範囲外です)。

+0

偉大な答えであり、 'async'と' await'が標準的になるのを見てうれしいです。私は、OPのコードがファイルシステムを操作しようとしていると仮定しました。リンク(シンボリックリンク)を無視すると、循環参照は*おそらく問題ではありません。また、あなたのStackOverflowフォーマットスキー場はチェーンから外れています! – Phil

+0

フィードバックのおかげで@Phil^_ ^ – naomik