さらに、読者には、肯定的な反応の場合、サイクルに発展する可能性のある記事が提供されます。私がこのサイクルを首尾よく書き、リーダーがそれを首尾よく習得した場合、それが何をするかだけでなく、それが内部でどのように機能するかについても次のコードについて明らかになるでしょう:
while (true) {
const data = yield getNextChunk(); //
const processed = processData(data);
try {
yield sendProcessedData(processed);
showOkResult();
} catch (err) {
showError();
}
}
これは最初のパイロット部分です:イテレーターとジェネレーター。
イテレーター
したがって、イテレータは、データへの順次アクセスを提供するインターフェイスです。
ご覧のとおり、この定義はデータやメモリ構造については何も述べていません。実際、未定義のシーケンスは、メモリスペースを占有せずにイテレータとして表すことができます。
私は読者に質問に答えることを提案します:配列はイテレーターですか?
回答
. shift pop .
では、言語の基本構造の1つである配列を使用して、データを順番に、または任意の順序で操作できるのに、なぜ反復子が必要なのでしょうか。
一連の自然数を実装するイテレーターが必要だと想像してみましょう。またはFibonacci番号。または他の無限のシーケンス。無限のシーケンスを配列に配置することは困難です。配列にデータを徐々に埋めるメカニズムと、プロセスメモリ全体を埋めないように古いデータを削除するメカニズムが必要です。これは不必要な複雑さであり、配列のないソリューションは複数の行に収まる可能性があるにもかかわらず、実装とサポートがさらに複雑になります。
const getNaturalRow = () => {
let current = 0;
return () => ++current;
};
また、イテレータは、Webソケットなどの外部チャネルからのデータの受信を表すことができます。
javascriptでは、イテレーターは、フィールド値(イテレーターの現在の値とdone)を持つ構造を返すnext()メソッドを持つオブジェクトです(この規則はECMAScript言語標準で説明されています)。このようなオブジェクトは、Iteratorインターフェイスを実装します。前の例を次の形式で書き直してみましょう。
const getNaturalRow = () => ({
_current: 0,
next() { return {
value: ++this._current,
done: false,
}},
});
JavascriptにはIterableインターフェイスもあります。これは、イテレーターを返す@@イテレーターメソッド(この定数はSymbol.iteratorとして使用可能)を持つオブジェクトです。このようなインターフェイスを実装するオブジェクトの場合、オペレータートラバーサルを使用できます
for..of。もう一度例を書き直してみましょう。今回はIterable実装としてのみです。
const naturalRowIterator = {
[Symbol.iterator]: () => ({
_current: 0,
next() { return {
value: ++this._current,
done: this._current > 3,
}},
}),
}
for (num of naturalRowIterator) {
console.log(num);
}
// : 1, 2, 3
ご覧のとおり、ある時点で完了フラグを正にする必要がありました。そうしないと、ループが無限になります。
ジェネレーター
ジェネレーターは、イテレーターの進化における次の段階になりました。それらは、関数値のような反復子値を返すための構文上の砂糖を提供します。ジェネレーターは、イテレーターを返す関数(アスタリスク:function *で宣言)です。この場合、イテレーターは明示的に返されません。関数は、yieldステートメントを使用してイテレーターの値のみを返します。関数が実行を終了すると、イテレーターは完了したと見なされます(次のメソッドへの後続の呼び出しの結果には、doneフラグがtrueに等しくなります)
function* naturalRowGenerator() {
let current = 1;
while (current <= 3) {
yield current;
current++;
}
}
for (num of naturalRowGenerator()) {
console.log(num);
}
// : 1, 2, 3
すでにこの単純な例では、ジェネレーターの主なニュアンスが目に見えています。ジェネレーター関数内のコードは同期的に実行されません。対応するイテレーターでnext()を呼び出した結果、ジェネレーターコードが段階的に実行されます。前の例でジェネレータコードがどのように実行されるかを見てみましょう。ジェネレータが停止した場所をマークするために、特別なカーソルを使用します。
naturalRowGeneratorが呼び出されると、イテレーターが作成されます。
function* naturalRowGenerator() {
▷let current = 1;
while (current <= 3) {
yield current;
current++;
}
}
さらに、最初の3回次のメソッドを呼び出すか、この場合はループを繰り返すと、カーソルはyieldステートメントの後に配置されます。
function* naturalRowGenerator() {
let current = 1;
while (current <= 3) {
yield current; ▷
current++;
}
}
そして、nextへの後続のすべての呼び出しについて、およびループを終了した後、ジェネレーターは実行を終了し、nextを呼び出した結果は次のようになります。
{ value: undefined, done: true }
イテレーターへのパラメーターの受け渡し
現在のカウンターをリセットし、最初からカウントを開始する機能を自然数のイテレーターに追加する必要があると想像してみましょう。
naturalRowIterator.next() // 1
naturalRowIterator.next() // 2
naturalRowIterator.next(true) // 1
naturalRowIterator.next() // 2
自作のイテレーターでそのようなパラメーターを処理する方法は明らかですが、ジェネレーターはどうですか?
ジェネレーターはパラメーターの受け渡しをサポートしていることがわかりました。
function* naturalRowGenerator() {
let current = 1;
while (true) {
const reset = yield current;
if (reset) {
current = 1;
} else {
current++;
}
}
}
渡されたパラメーターは、yieldステートメントの結果として使用可能になります。カーソルアプローチで明快さを加えてみましょう。イテレーターが作成されたとき、何も変更されていません。これに続いて、next()メソッドへの最初の呼び出しが行われます。
function* naturalRowGenerator() {
let current = 1;
while (true) {
const reset = ▷yield current;
if (reset) {
current = 1;
} else {
current++;
}
}
}
カーソルは、yieldステートメントから戻った瞬間にフリーズしました。nextへの次の呼び出しで、関数に渡された値がリセット変数の値を設定します。まだyieldの呼び出しがないので、次の最初の呼び出しで渡された値はどこに行き着くのでしょうか。どこにも!広大なガベージコレクターに溶け込みます。ジェネレーターに初期値を渡す必要がある場合は、ジェネレーター自体の引数を使用してこれを行うことができます。例:
function* naturalRowGenerator(start = 1) {
let current = start;
while (true) {
const reset = yield current;
if (reset) {
current = start;
} else {
current++;
}
}
}
const iterator = naturalRowGenerator(10);
iterator.next() // 10
iterator.next() // 11
iterator.next(true) // 10
結論
イテレーターの概念とjavascript言語での実装について説明しました。また、ジェネレーター(イテレーターを便利に実装するための構文構造)についても学習しました。
この記事では番号シーケンスの例を示しましたが、javascriptイテレーターはさらに多くのことを実行できます。それらは、データの任意のシーケンス、さらには多くの有限状態マシンを表すことができます。次の記事では、ジェネレーターを使用して非同期プロセス(coroutines、goroutines、cspなど)を構築する方法について説明します。