ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript ジェネレーター: 一時停止機能でコードを強化しましょう!
JavaScript ジェネレーターは非常に優れています。これらは通常の機能に似ていますが、スーパーパワーが備わっています。私は最近これらを頻繁に使用していますが、コード内の制御フローについての考え方が変わりました。
基本から始めましょう。ジェネレーターは、一時停止および再開できる機能です。 function* 構文と yield キーワードを使用します。簡単な例を次に示します:
function* countToThree() { yield 1; yield 2; yield 3; } const counter = countToThree(); console.log(counter.next().value); // 1 console.log(counter.next().value); // 2 console.log(counter.next().value); // 3
関数を一度に 1 つの結果ずつステップ実行する方法がわかりましたか?それがジェネレーターの魔法です。
しかし、ジェネレーターは単にカウントするだけではありません。これらはカスタム反復子の作成に最適です。フィボナッチ数列を生成したいとします:
function* fibonacci() { let [prev, curr] = [0, 1]; while (true) { yield curr; [prev, curr] = [curr, prev + curr]; } } const fib = fibonacci(); for (let i = 0; i < 10; i++) { console.log(fib.next().value); }
このジェネレーターはフィボナッチ数を永遠に生成し続けます。これは無限シーケンスですが、必要な値のみを計算します。
ここで、ジェネレーターの最も優れた点の 1 つである遅延評価について説明します。これらは、要求された場合にのみ値を計算します。これは、大規模なデータセットや複雑な計算の場合に非常に効率的です。
より実践的な例を見てみましょう。大規模なデータセット用のページネーション システムを構築しているとします:
function* paginate(items, pageSize) { for (let i = 0; i < items.length; i += pageSize) { yield items.slice(i, i + pageSize); } } const allItems = Array.from({ length: 100 }, (_, i) => i + 1); const pageSize = 10; const pages = paginate(allItems, pageSize); console.log(pages.next().value); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] console.log(pages.next().value); // [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
このジェネレーターを使用すると、すべてを一度にメモリにロードすることなく、必要に応じてデータのページをフェッチできます。
ジェネレーターは、非同期プログラミングに関しても威力を発揮します。非同期コードを同期コードのように見せ、動作させることができます。 co ライブラリを使用した例を次に示します。
const co = require('co'); function* fetchUserData() { const user = yield fetchUser(); const posts = yield fetchPosts(user.id); const comments = yield fetchComments(posts[0].id); return { user, posts, comments }; } co(fetchUserData).then(result => { console.log(result); }).catch(error => { console.error(error); });
このコードは同期的に見えますが、実際には 3 つの非同期呼び出しを行っています。ジェネレーターは、Promise が解決されるまで、各利回りで一時停止します。
ジェネレーターは、共同マルチタスクにも使用できます。複数のジェネレーターを作成し、それらを切り替えて、同時実行をシミュレートできます。
function* task1() { yield 'Start task 1'; yield 'Middle of task 1'; yield 'End task 1'; } function* task2() { yield 'Start task 2'; yield 'Middle of task 2'; yield 'End task 2'; } function run(tasks) { const iterations = tasks.map(task => task()); while (iterations.length) { const [first, ...rest] = iterations; const { value, done } = first.next(); if (!done) { console.log(value); iterations.push(first); } iterations.unshift(...rest); } } run([task1, task2]);
このコードは 2 つのタスクを交互に実行し、一度に 1 ステップずつ実行します。
ジェネレーターはステートマシンの作成にも最適です。各収量は異なる状態を表すことができます:
function* trafficLight() { while (true) { yield 'red'; yield 'green'; yield 'yellow'; } } const light = trafficLight(); console.log(light.next().value); // red console.log(light.next().value); // green console.log(light.next().value); // yellow console.log(light.next().value); // red
この信号機は州を無期限に循環し続けます。
ここで、より高度なテクニックについて話しましょう。ジェネレーターの委任により、別のジェネレーターに譲歩することができます:
function* innerGenerator() { yield 'inner 1'; yield 'inner 2'; } function* outerGenerator() { yield 'outer 1'; yield* innerGenerator(); yield 'outer 2'; } const gen = outerGenerator(); console.log(gen.next().value); // outer 1 console.log(gen.next().value); // inner 1 console.log(gen.next().value); // inner 2 console.log(gen.next().value); // outer 2
yield* 構文は innerGenerator に委譲し、そのすべての値を生成してから、outerGenerator を続行します。
ジェネレーターでのエラー処理についても言及する価値があります。 throw() メソッドを使用して、エラーをジェネレーターにスローできます。
function* errorGenerator() { try { yield 'Start'; yield 'Middle'; yield 'End'; } catch (error) { console.error('Caught:', error); yield 'Error handled'; } } const gen = errorGenerator(); console.log(gen.next().value); // Start console.log(gen.throw(new Error('Oops!')).value); // Caught: Error: Oops! // Error handled
これにより、非常に高度なエラー処理戦略が可能になります。
ジェネレーターはバックトラッキング アルゴリズムの実装にも使用できます。以下は、項目の可能なすべての組み合わせを生成する簡単な例です:
function* countToThree() { yield 1; yield 2; yield 3; } const counter = countToThree(); console.log(counter.next().value); // 1 console.log(counter.next().value); // 2 console.log(counter.next().value); // 3
このジェネレーターは、指定されたアイテムの 2 要素の組み合わせをすべて生成します。
ジェネレーターが真に威力を発揮する領域の 1 つは、大量のデータの処理です。必要なものだけを計算する効率的なデータ処理パイプラインを作成できます。これは、大きなファイルを 1 行ずつ処理する例です:
function* fibonacci() { let [prev, curr] = [0, 1]; while (true) { yield curr; [prev, curr] = [curr, prev + curr]; } } const fib = fibonacci(); for (let i = 0; i < 10; i++) { console.log(fib.next().value); }
このジェネレーターはファイルを 1 行ずつ読み取るため、巨大なファイルを完全にメモリにロードせずに処理できます。
ジェネレーターを使用して、Observable パターンを実装することもできます。簡単な実装は次のとおりです。
function* paginate(items, pageSize) { for (let i = 0; i < items.length; i += pageSize) { yield items.slice(i, i + pageSize); } } const allItems = Array.from({ length: 100 }, (_, i) => i + 1); const pageSize = 10; const pages = paginate(allItems, pageSize); console.log(pages.next().value); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] console.log(pages.next().value); // [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
このジェネレーターはオブザーバーとして機能し、受信した各データを処理します。
ジェネレーターのもう 1 つの優れた使用法は、カスタム制御構造を作成することです。以下は、ジェネレーターを使用した再試行関数の例です。
const co = require('co'); function* fetchUserData() { const user = yield fetchUser(); const posts = yield fetchPosts(user.id); const comments = yield fetchComments(posts[0].id); return { user, posts, comments }; } co(fetchUserData).then(result => { console.log(result); }).catch(error => { console.error(error); });
この再試行関数は、指定された関数を最大 maxAttempts 回試行してから、あきらめます。
結論として、ジェネレーターは、より表現力豊かで効率的で保守しやすいコードを作成するのに役立つ JavaScript の強力な機能です。これらは、複雑な制御フローの処理、非同期操作の管理、大規模なデータセットの処理に最適です。最初は少し奇妙に思えるかもしれませんが、コツを掴めば、プロジェクトでそれらを使用するあらゆる種類の創造的な方法が見つかるでしょう。それでは、次の JavaScript プロジェクトでジェネレーターを試してみてください。コードがどれほど簡素化されるかに驚くかもしれません!
私たちの作品をぜひチェックしてください:
インベスターセントラル | スマートな暮らし | エポックとエコー | 不可解な謎 | ヒンドゥーヴァ | エリート開発者 | JS スクール
Tech Koala Insights | エポックズ&エコーズワールド | インベスター・セントラル・メディア | 不可解な謎 中 | 科学とエポックミディアム | 現代ヒンドゥーヴァ
以上がJavaScript ジェネレーター: 一時停止機能でコードを強化しましょう!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。