Iterator-Observer 介面
發電機是兩件事的組合 - 一個 Iterator
和一個 Observer
。
迭代器
迭代器是被呼叫時返回 iterable
的東西。一個 iterable
是你可以迭代的東西。從 ES6 / ES2015 開始,所有集合(Array, Map, Set, WeakMap, WeakSet)都符合 Iterable 合同。
生成器(迭代器)是生產者。在迭代中,消費者來自生產者的價值。
例:
function *gen() { yield 5; yield 6; }
let a = gen();
每當你呼叫 a.next()
時,你實際上都是從迭代器中獲取價值,而在 yield
中執行的是 tihuan。下次呼叫 a.next()
時,執行將從先前暫停的狀態恢復。
觀察
生成器也是一個觀察者,你可以使用它將一些值傳送回生成器。
function *gen() {
document.write('<br>observer:', yield 1);
}
var a = gen();
var i = a.next();
while(!i.done) {
document.write('<br>iterator:', i.value);
i = a.next(100);
}
在這裡你可以看到 yield 1
像一個表示式一樣被用來計算某個值。它評估的值是作為引數傳送到 a.next
函式呼叫的值。
因此,第一次 i.value
將是第一個產生的值(1
),並且當繼續迭代到下一個狀態時,我們使用 a.next(100)
將值傳送回生成器。
與生成器進行非同步
生成器廣泛用於 spawn
(來自 taskJS 或 co)函式,其中函式接收生成器並允許我們以同步方式編寫非同步程式碼。這並不意味著非同步程式碼被轉換為同步程式碼/同步執行。這意味著我們可以編寫看起來像 sync
的程式碼,但在內部它仍然是 async
。
同步是阻止; 非同步是等待。編寫阻塞程式碼很容易。拉出時,值出現在分配位置。當 PUSHing 時,值出現在回撥的引數位置。
當你使用迭代器時,你需要生成器的值。當你使用回撥時,生產者 tihuan 將值轉換為回撥的引數位置。
var i = a.next() // PULL
dosomething(..., v => {...}) // PUSH
在這裡,你從 a.next()
中提取值,在第二個中,v => {...}
是回撥,並且值被轉換為回撥函式的引數位置 v
。
使用這種推拉機制,我們可以編寫這樣的非同步程式設計,
let delay = t => new Promise(r => setTimeout(r, t));
spawn(function*() {
// wait for 100 ms and send 1
let x = yield delay(100).then(() => 1);
console.log(x); // 1
// wait for 100 ms and send 2
let y = yield delay(100).then(() => 2);
console.log(y); // 2
});
因此,看看上面的程式碼,我們編寫的非同步程式碼看起來像是 blocking
(yield 語句等待 100ms,然後繼續執行),但它實際上是 waiting
。發電機的 pause
和 resume
屬性允許我們做這個驚人的技巧。
它是如何工作的?
spawn 函式使用 yield promise
從生成器中拉出 promise 狀態,等待 promise 被解析,並將解析後的值推回給生成器,以便它可以使用它。
現在就用它
因此,使用生成器和 spawn 函式,你可以清理 NodeJS 中的所有非同步程式碼,使其看起來和感覺它是同步的。這將使除錯變得容易。程式碼看起來也很整潔。
這個功能將在未來版本的 JavaScript 中出現 - 如 async...await
。但是你現在可以使用庫中定義的 spawn 函式在 ES2015 / ES6 中使用它們 - taskjs,co 或 bluebird