Generators是Javascript的一種協同程式( coroutine 簡稱:協程)風格,是指那些可以在執行時暫停然後又恢複的函數,該函數是在functi配以星號符號形式如function* ,函數內有些特徵關鍵詞如yield 和yield*。
function* generatorFn () { console.log('look ma I was suspended')}var generator = generatorFn() // [1]setTimeout(function () { generator.next() // [2]}, 2000)
對代碼中標註的[1]和[2]解釋如下:
1. 這是一個generator以暫停方式開始. 這時沒有控制台輸出。
2.通過調用其next()方法,這個generator才會執行,運行直至它碰到下一個yield關鍵詞或return,現在我們就有了控制台輸出。
再看一個案例:
function *generator() { console.log('Start!'); var i = 0; while (true) { if (i < 3) yield i++; }}var gen = generator();
以上這段代碼類似第一個,只是在generator函數中多了yield關鍵詞,以上這段代碼被調用時,不會立即執行,而是暫停待命的狀態,因此不會有Start輸出。直到其next()調用才執行。
var ret = gen.next();// Start!console.log(ret);// {value: 0, done: false}
上面ret是generator結果. 它有兩個屬性:
■value, 在generator函數中的yield值,
■done, 這是一個標識表示generator函數是否返回.
繼續代碼如下:
console.log(gen.next());// {value: 1, done: false}console.log(gen.next());// {value: 2, done: false}console.log(gen.next());// {value: undefined, done: true}
generator在同步編程中沒有什麼玄機,特別適合在非同步編程中。
generator有兩個特點:
1.能選擇跳出一個函數,讓外部代碼決定什麼時候再跳回這個函數繼續執行。
2.能夠進行非同步控制。
看下面非同步執行代碼:
var gen = generator();console.log(gen.next().value);setTimeout(function() { console.log(gen.next().value); console.log('第一步');}, 1000);console.log('第二步');
輸出是:
0
第二步
1
第一步
也就是說,不會在setTimeout這裡等待計時結束,而是直接繼續“第二步”,不會在setTimeout堵塞。
再看另外一段代碼:
function* channel () { var name = yield 'hello, what is your name?' // [1] return 'well hi there ' + name}var gen = channel()console.log(gen.next().value) // hello, what is your name? [2]console.log(gen.next('billy')) // well hi there billy [3]
在遍曆時也可以使用*:
function* iter () { for (var i = 0; i < 10; i++) yield i}for (var val of iter()) { console.log(val) // outputs 1?—?9}
普遍的誤解
既然我可以暫停一個函數執行,那麼是不是讓它們並存執行呢?不是,因為Javascript是一個單線程,如果你想尋求提升效能,generator並不是你的菜。
比如下面代碼分別執行斐波那契數:
function fib (n) { var current = 0, next = 1, swap for (var i = 0; i < n; i++) { swap = current, current = next next = swap + next } return current} function* fibGen (n) { var current = 0, next = 1, swap for (var i = 0; i < n; i++) { swap = current, current = next next = swap + next yield current }}
效能結果如下:(越高越好)
results:
regular 1263899
generator 37541
generators閃亮點
Generators 能簡化JavaScript中函數的複雜性。
懶賦值
懶賦值雖然可以使用JS的閉包實現,但是使用yield會有很大的簡化,通過暫停和恢複,我們能夠在我們需要的時候擷取數值,比如上面fibGen函數可以在我們需要時拉取新值:
var fibIter = fibGen(20)var next = fibIter.next()console.log(next.value)setTimeout(function () { var next = fibIter.next() console.log(next.value)},2000)當然還使用for迴圈:依然是懶賦值for (var n of fibGen(20) { console.log(n)}
無限序列
因為可以懶賦值,那麼可能表演一些Haskell招數, 類似infinite sequences. 這裡能夠yield一個無限序列的數量。
function* fibGen () { var current = 0, next = 1, swap while (true) { swap = current, current = next next = swap + next yield current }}
我們看看一個斐波那契數流的懶賦值,要求它返回5000以後的第一個斐波那契數:
for (var num of fibGen()) { if (num > 5000) break}console.log(num) // 6765
非同步流程式控制制
使用generators實現非同步流程式控制制,最常見是各種 promise庫包,那麼它是如何工作呢?
在Node領域,每個事情都是和回調有關,這是我們的低層次非同步功能,我們可以使用generators 建立一個通訊通道,從而使用同步編程的風格編寫非同步代碼。
run(function* () { console.log("Starting") var file = yield readFile("./async.js") // [1] console.log(file.toString())})
注釋1表示程式會在等待async.js返回結果以後再繼續。
genify是一個將generators帶入平常編程環境的架構,使用如下:
npm install genify 進行安裝,代碼如下:
var Q = require('q');var fs = require('fs');var genify = require('genify'); // wrap your object into genify functionvar object = genify({ concatFiles: function * (file1, file2, outFile) { file1 = yield Q.nfcall(fs.readFile, file1); file2 = yield Q.nfcall(fs.readFile, file2); var concated = file1 + file2; yield Q.nfcall(fs.writeFile, outFile, concated); return concated; }}); // concatFiles是一個generator函數,它使用generator強大能力。object.concatFiles('./somefile1.txt', './somefile2.txt', './concated.txt').then(function (res) { // do something with result}, function (err) { // do something with error});
以上這篇在Node.js中使用Javascript Generators詳解就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援雲棲社區。