2017-02-07 5 views
2

yieldキーワードの主な目的は、非同期ループを作成するためにそれを使用することもかなり便利ですが、いくつかのデータの上にイテレータを提供することであるが:yieldをコールバックベースのループで使用する方法は?

function* bigLoop() { 
    // Some nested loops 
    for(...) { 
     for(...) { 
      // Yields current progress, eg. when parsing file 
      // or processing an image       
      yield percentCompleted; 
     } 
    } 
} 

これはその後、非同期的に呼び出すことができます。

function big_loop_async(delay) { 
    var iterator = big_loop(); 
    function doNext() { 
     var next = iterator.next(); 
     var percent_done = next.done?100:next.value; 
     console.log(percent_done, " % done."); 
     // start next iteration after delay, allowing other events to be processed 
     if(!next.done) 
      setTimeout(doNext, delay); 
    } 
    setTimeout(doNext, delay); 
} 

しかし、現代のjavascriptでは、コールバックベースのループがかなり普及しています。我々はArray.prototype.forEachArray.prototype.findまたはArray.prototype.sortを持っています。これらのすべては、各反復ごとに渡されるコールバックに基づいています。可能ならばこれらを使用することが推奨されると聞いていました。なぜなら、ループの標準よりも最適化できるからです。

また、コールバックベースのループを使用して、複雑なループパターンを抽象化することもよくあります。

ここでの質問は、yieldベースのイテレータにすることができますか?簡単な例として、配列を非同期でソートしたいと考えています。

+0

、どんなメリットがありますか? –

+0

@DavinTryonこれは* example *の例ですが、実際の例は分かりにくいものです。とにかく、配列が長すぎると並べ替えに時間がかかりすぎる可能性があります。たとえば、サーバーとの接続を切断したり、ユーザーのブラウザを遅らせることができます。 –

+0

@TomášZatoこれは[良い記事]です(http://raganwald.com/2016/05/07/javascript-generators-for-people-who-dont-give-a-shit-about-getting-stuff-done .html)あなたは興味深い(不幸なタイトル)を見つけるかもしれません。そう、あなたのOPへの短い答え、それははいできます。 –

答えて

1

TL; DR:あなたはそれを行うが、あなたはは、最新のV8とbluebirdで行うことができます。この他のことをチェックアウトすることはできません。

async function asyncReduce() { 
    const sum = await Promise.reduce(
     [1, 2, 3, 4, 5], 
     async (m, n) => m + await Promise.delay(200, n), 
     0 
    ); 

    console.log(sum); 
} 

いいえ、そうではありませんArray.prototype.sortは、その比較関数から非同期的に比較結果を受け入れることができます。それを完全に再実装する必要があります。

function syncForEach() { 
    [1, 2, 3, 4, 5].forEach(function (x) { 
     console.log(x); 
    }); 
} 
:他の個々のケースでは、(あなたが期待するように、1つは yieldから続行する前に、すべての発電機は、その最初の yieldまで実行されますので、必ずしもでも、動作しません)coroutiney forEachのようなハッキング、あるかもしれません(パフォーマンスを見てまで)自然がうまく動作
function delayed(x) { 
    return new Promise(resolve => { 
     setTimeout(() => resolve(x), Math.random() * 1000 | 0); 
    }); 
} 

function* chain(iterators) { 
    for (const it of iterators) { 
     yield* it; 
    } 
} 

function* asyncForEach() { 
    yield* chain(
     [1, 2, 3, 4, 5].map(function* (x) { 
      console.log(yield delayed(x)); 
     }) 
    ); 
} 

reduce、:

function syncReduce() { 
    const sum = [1, 2, 3, 4, 5].reduce(function (m, n) { 
     return m + n; 
    }, 0); 

    console.log(sum); 
} 
function* asyncReduce() { 
    const sum = yield* [1, 2, 3, 4, 5].reduce(function* (m, n) { 
     return (yield* m) + (yield delayed(n)); 
    }, function*() { return 0; }()); 

    console.log(sum); 
} 

しかし、すべての機能で魔法の杖はありません。

理想的には、あなたはすべてのこれらの関数の代替約束ベースの実装を追加したい - 、青い鳥のような人気の約束ライブラリを、すでに例えば、mapreduceのためにこれを行う - とasync機能するのでasync/await代わりの発電機を(使用)約束を返す:

async function asyncReduce() { 
    const sum = await Promise.reduce(
     [1, 2, 3, 4, 5], 
     async (m, n) => m + await delayed(n), 
     0 
    ); 

    console.log(sum); 
} 

あなたはどちらか、ECMAScriptのは、Pythonのようなまともなデコレータを持っていた場合は、このあまりを行うにはasyncサポートを待つ必要はありません。

@Promise.coroutine 
function* add(m, n) { 
    return m + (yield delayed(n)); 
} 

@Promise.coroutine 
function* asyncReduce() { 
    const sum = yield Promise.reduce([1, 2, 3, 4, 5], add, 0); 

    console.log(sum); 
} 

...でもそうはしません。それとも、このようなコードと一緒に暮らすことができますでしょうが(発電機とJSのシングルスレッドで)非同期に配列をソートする

const asyncReduce = Promise.coroutine(function*() { 
    const sum = yield Promise.reduce([1, 2, 3, 4, 5], Promise.coroutine(function* (m, n) { 
     return m + (yield delayed(n)); 
    }), 0); 

    console.log(sum); 
}); 
関連する問題