2017-02-07 7 views
2

Obwohl der Hauptzweck der Ausbeute Schlüsselwort ist Iteratoren über einige Daten zur Verfügung zu stellen, ist es auch sehr bequem ist, es zu benutzen asynchrone Schleifen zu erstellen:Wie verwendet man die Rendite mit Callback-basierten Loops?

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); 
} 
:

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

Dies kann dann asynchron aufgerufen werden

In modernen Javascript sind Callback-basierte Loops jedoch recht populär geworden. Wir haben Array.prototype.forEach, Array.prototype.find oder Array.prototype.sort. Alle diese basieren auf einem Rückruf, der für jede Iteration übergeben wird. Ich habe sogar gehört, dass es empfehlenswert ist, dass wir diese verwenden, wenn wir können, weil sie besser als Standard für Schleifen optimiert werden können.

Ich verwende auch häufig Callback-basierte Loops, um einige komplexe Looping-Muster zu abstrahieren.

Und die Frage ist, ist es möglich, diese in yield basierte Iteratoren zu verwandeln? Betrachten Sie als einfaches Beispiel, dass Sie ein Array asynchron sortieren möchten.

+0

Würde ein Array asynchron (mit einem Generator und einem einzelnen Thread von js) sortieren, irgendwelche Vorteile haben? –

+0

@DavinTryon Es ist ein * Beispiel *, Beispiele sind nicht entworfen, um praktisch zu sein, aber um einfach zu verstehen. Wie auch immer, ein Array, das lange genug ist, könnte zu lange dauern, um es zu sortieren, und könnte beispielsweise Personen von Ihrem Server trennen oder den Browser des Benutzers verzögern. –

+0

@ TomášZato Dies ist ein [guter Artikel] (http://raganwald.com/2016/05/07/jacascript-generators-for-people-who-dont-give-a-shit-about-getting-stuff-done .html), die Sie vielleicht interessant finden (unglücklicher Titel). Also, kurze Antwort auf Ihre OP, ist ja, es kann getan werden. –

Antwort

1

tl; dr: Sie kann das nicht tun, aber diese andere Sache überprüfen Sie können mit der neuesten V8 tun und 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); 
} 

Nein, es ist nicht möglich, Array.prototype.sort die Ergebnisse von Vergleichen asynchron von seiner Vergleichsfunktion zu akzeptieren; Sie müssten es vollständig neu implementieren. Für andere Einzelfällen kann es Hacks sein, wie ein coroutiney forEach (die nicht notwendigerweise auch arbeiten, wie man erwarten würde, denn jeder Generator, bis seine laufen erste yield bevor man weiterhin von einem yield):

function syncForEach() { 
    [1, 2, 3, 4, 5].forEach(function (x) { 
     console.log(x); 
    }); 
} 
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)); 
     }) 
    ); 
} 

und reduce, die schön durch die Natur funktioniert (bis auf die Leistung schauen):

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); 
} 

aber ja, kein Zauberstab für alle Funktionen.

Idealerweise würden Sie alternative Versprechen basierte Implementierungen für alle diese Funktionen hinzufügen - beliebte Versprechen Bibliotheken, wie drossel, tun dies bereits für map und reduce, zum Beispiel - und async/await statt Generatoren verwenden (da async Funktionen Rückkehr Versprechen):

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

    console.log(sum); 
} 

Sie würden nicht für async Unterstützung warten müssen, um diese so viel zu tun, wenn ECMAScript sane Dekorateure wie Python hatte, entweder:

@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); 
} 

... aber es tut nicht und so tun Sie.Oder können Sie mit Code wie folgt leben:

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); 
});