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。發電機的 pauseresume 屬性允許我們做這個驚人的技巧。

它是如何工作的?

spawn 函式使用 yield promise 從生成器中拉出 promise 狀態,等待 promise 被解析,並將解析後的值推回給生成器,以便它可以使用它。

現在就用它

因此,使用生成器和 spawn 函式,你可以清理 NodeJS 中的所有非同步程式碼,使其看起來和感覺它是同步的。這將使除錯變得容易。程式碼看起來也很整潔。

這個功能將在未來版本的 JavaScript 中出現 - 如 async...await。但是你現在可以使用庫中定義的 spawn 函式在 ES2015 / ES6 中使用它們 - taskjs,co 或 bluebird