の非同期発電機とパイプライン
27552 ワード
非同期発電機の導入
この記事と最後の1つは、非同期イテレータを扱っていますが、いくつかのプログラミングをしていたときに発生した問題によって動機づけられました
async
機能:それは可能ですかyield
インasync
機能?言い換えると、我々はasync
関数は、発電機の機能ですか?この問題を調査するために、通常の同期発電機関数から始めましょう.
numberGenerator
:const random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
const getValue = () => {
return random(1,10)
}
const numberGenerator = function* () {
for (let i=0; i<5; i++) {
const value = getValue()
yield value**2
}
}
const main = () => {
const numbers = numberGenerator()
for (const v of numbers) {
console.log('number = ' + v)
}
}
main()
このコードは5つの乱数の予想される正方形を生成します.C:\dev>node gen.js
number = 1
number = 64
number = 36
number = 25
number = 49
私の考えは変更することでしたgetValue
約束を返すnumberGenerator
to await
この約束はyield
値.次のように試しました.const random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
const getValue = () => {
//return promise instead of value
return new Promise(resolve=>{
setTimeout(()=>resolve(random(1,10)), 1000)
})
}
const numberGenerator = function* () {
for (let i=0; i<5; i++) {
const value = await getValue() //await promise
yield value**2
}
}
const main = () => {
const numbers = numberGenerator()
for (const v of numbers) {
console.log('number = ' + v)
}
}
main()
何が起こるか見ましょうC:\dev\gen.js:12
const value = await getValue() //await promise
^^^^^
SyntaxError: await is only valid in async function
at new Script (vm.js:51:7)
さて、それは意味をなす.我々は我々を作る必要があるnumberGenerator
機能async
. 試してみましょう!const numberGenerator = async function* () { //added async
仕事ですか.C:\dev\gen.js:10
const numberGenerator = async function* () { //added async
^
SyntaxError: Unexpected token *
at new Script (vm.js:51:7)
ああ、それは動作しませんでした.これは、私が何かをオンラインでいくつかのトピックを検索する主導です.この種の機能性があると判明released in ES2018 , そして、私たちはそれを使用することができます--harmony-async-iteration
フラグ.アクションでこれを見ましょう.
const timer = () => setInterval(()=>console.log('tick'), 1000)
const random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
const getValue = () => {
//return promise instead of value
return new Promise(resolve=>{
setTimeout(()=>resolve(random(1,10)), 1000)
})
}
const numberGenerator = async function* () { //added async
for (let i=0; i<5; i++) {
const value = await getValue() //await promise
yield value**2
}
}
//main is 'async'
const main = async () => {
const t = timer()
const numbers = numberGenerator()
//use 'for await...of' instead of 'for...of'
for await (const v of numbers) {
console.log('number = ' + v)
}
clearInterval(t)
}
main()
コードの以前のバージョンからいくつかの小さな変更があります.main
関数のfor...of
ループになるfor await...of
ループ.await
, main
としてマークasync
A timer was also added so we can confirm that the generator is indeed asynchronous.
結果を見てみましょう.
C:\dev>node --harmony-async-iteration gen.js
tick
number = 16
tick
number = 1
tick
number = 100
tick
number = 100
tick
number = 49
それは働いた!The
yield
in anasync
generator function is similar to theyield
in a normal (synchronous) generator function. The difference is that in the regular version,yield
produces a{value, done}
tuple, whereas the asynchronous version produces a promise that resolves to a{value, done}
tuple.If you
yield
a promise, the JavaScript runtimes does something a bit sneaky: It still produces its own promise that resolves to a{value, done}
tuple, but thevalue
attribute in that tuple will be whatever your promise resolves to.
パイプライン同期非同期発電機
この技術のきちんとした小さなアプリケーションを見てみましょう:我々は非同期の発電機関数を作成します.
この種のパイプラインは、非同期データストリームの任意の変換を実行するために用いることができる.
最初に、値の無限のストリームを生成する非同期発電機を記述します.毎秒0と100の間にランダムな値を生成します.
const random = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
const asyncNumberGenerator = async function* () {
while (true) {
const randomValue = random(0,100)
const p = new Promise(resolve=>{
setTimeout(()=>resolve(randomValue), 1000)
})
yield p
}
}
次に関数を書きます.createStatsReducer
. この関数はコールバック関数を返します.exponentialStatsReducer
, これはデータのストリームを繰り返し計算するために使用されます.const createStatsReducer = alpha => {
const beta = 1 - alpha
const exponentialStatsReducer = (newValue, accumulator) => {
const redistributedMean = beta * accumulator.mean
const meanIncrement = alpha * newValue
const newMean = redistributedMean + meanIncrement
const varianceIncrement = alpha * (newValue - accumulator.mean)**2
const newVariance = beta * (accumulator.variance + varianceIncrement)
return {
lastValue: newValue,
mean: newMean,
variance: newVariance
}
}
return exponentialStatsReducer
}
次に、第二の非同期発電機関数があります.asyncReduce
. これは非同期iterableに還元器を適用します.JavaScriptの組み込みのように動作します Array.prototype.reduce
. しかし、標準バージョンは、最終的な値を生成するために、配列全体を通過しますが、我々のバージョンは、lazily削減を適用します.これにより、データソースとして無限の値のシーケンス(上記の非同期数発生器)を使用できます.const asyncReduce = async function* (iterable, reducer, accumulator) {
for await (const item of iterable) {
const reductionResult = reducer(item, accumulator)
accumulator = reductionResult
yield reductionResult
}
}
いっしょに結びましょう.以下のコードは、非同期に生成された数の無限のシーケンスを、非同期の削減にパイプします.新しい値が到着すると、更新された平均、分散、および標準偏差を取得します.const timer = () => setInterval(()=>console.log('tick'), 1000)
const main = async () => {
const t = timer()
const numbers = asyncNumberGenerator()
const firstValue = await numbers.next()
//initialize the mean to the first value
const initialValue = { mean: firstValue.value, variance: 0 }
console.log('first value = ' + firstValue.value)
const statsReducer = createStatsReducer(0.1)
const reducedValues = asyncReduce(numbers, statsReducer, initialValue)
for await (const v of reducedValues) {
const lastValue = v.lastValue
const mean = v.mean.toFixed(2)
const variance = v.variance.toFixed(2)
const stdev = Math.sqrt(v.variance).toFixed(2)
console.log(`last value = ${lastValue}, stats = { mean: ${mean}`
+ `, variance: ${variance}, stdev: ${stdev} }`)
}
clearInterval(t)
}
main()
いくつかのサンプル出力を見てみましょう.C:\dev>node --harmony-async-iteration async_stats.js
tick
first value = 51
tick
last value = 97, stats = { mean: 55.60, variance: 190.44, stdev: 13.80 }
tick
last value = 73, stats = { mean: 57.34, variance: 198.64, stdev: 14.09 }
tick
last value = 11, stats = { mean: 52.71, variance: 372.05, stdev: 19.29 }
tick
last value = 42, stats = { mean: 51.64, variance: 345.16, stdev: 18.58 }
tick
last value = 42, stats = { mean: 50.67, variance: 319.00, stdev: 17.86 }
tick
last value = 60, stats = { mean: 51.60, variance: 294.93, stdev: 17.17 }
^C
我々は現在、継続的に値の我々の非同期ストリーム上の統計情報を更新取得します.きちんと!非同期式の関数は、これらの行に沿った非同期データのソースに対する処理を行うのに特に役立つと思います.
私はあなたが何を考えるか、またはあなたが他の方法のためのアイデアがある場合は非同期ジェネレータとイテレータを使用することができます教えてください!
参考文献
Reference
この問題について(の非同期発電機とパイプライン), 我々は、より多くの情報をここで見つけました https://dev.to/nestedsoftware/asynchronous-generators-and-pipelines-in-javascript--1h62テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol