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
看看我們如何一次單步執行一個yield函數?這就是生成器的魔力。
但是生成器的功能遠不止於計數。它們非常適合建立自訂迭代器。假設您要產生斐波那契數列:
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); }
這個生成器將永遠持續產生斐波那契數。這是一個無限序列,但我們只計算我們需要的值。
這為我們帶來了生成器最酷的事情之一:惰性求值。他們僅在我們要求時計算值。這對於大型資料集或複雜的計算來說非常有效率。
讓我們來看一個更實際的例子。假設您正在為大型資料集建立分頁系統:
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); });
這段程式碼看起來是同步的,但實際上它進行了三個非同步呼叫。生成器在每次收益處暫停,直到承諾解決。
產生器也可以用於協作多工處理。您可以建立多個生成器並在它們之間切換,模擬並發執行:
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]);
此程式碼在兩個任務之間交替,一次執行每個步驟。
生成器也非常適合建立狀態機。每個收益可以代表不同的狀態:
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 元素組合。
生成器真正發揮作用的一個領域是處理大量資料。您可以建立僅計算所需內容的高效資料處理管道。這是一個逐行處理大檔案的範例:
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); }
該生成器逐行讀取文件,允許您處理大文件,而無需將它們完全加載到記憶體中。
生成器也可以用來實作 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]
該生成器充當觀察者,處理它接收到的每個資料。
生成器的另一個很酷的用途是建立自訂控制結構。這是使用生成器的重試函數的範例:
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學校
科技無尾熊洞察 | 時代與迴響世界 | 投資人中央媒體 | 令人費解的謎團 | | 令人費解的謎團 | >科學與時代媒介 |
現代印度教以上是JavaScript 產生器:透過暫停功能增強您的程式碼!的詳細內容。更多資訊請關注PHP中文網其他相關文章!