ES6中引入了許多新特性,目前大量的JavaScript項目已經使用了ES6來進行開發,那麼熟悉這些新的特性是十分必要的,例如Redux-Saga中大量的使用了Iterator和generator。這篇文章總結和介紹一下ES6中的Iterator和Generator。chrome
第一個問題什麼是iterator?答案很簡單, Iterator是一個object,可是含有特定的接口,它有next method能夠返回一個result object,這個result object有兩個屬性第一個是value,表明這個迭代的值, 第二個是done,表明迭代是否結束。若是咱們本身來簡單實現一個Iterator,它是這樣的。json
function createIterator(items) { var i = 0; return { next : function () { var done = i >= (items.length) var value = items[i++] return { done: done, value: value } } } } const items = [1,2,3] const iteratorA = createIterator(items) iteratorA.next() // {result:1, done: false}
那麼Generator又是什麼?Generator 是一個函數能夠產生iterator。Generator函數用function關鍵字後邊帶*來表示。在函數定義上使用yield關鍵字來表示next方法調用時返回的值。例如異步
function *createIterator(){ yield 1; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next().value); //1 console.log(iterator.next().value); //2 console.log(iterator.next().value); //3
上邊介紹了什麼是Iterator,什麼是generator,下邊再介紹一個概念iterable。iterable是一個有Symbol.iterator屬性的object。這個symbol指向一個generator函數,這個函數返回關於這個對象的iterator。在ES6中全部的集合類對象(array, set, maps)和字符串都是iterable,而且有本身默認的iterator。當咱們在使用 for-of時候其實是利用了這些對象上的iterator,每次調用了next方法,將返回的result上的value返回回來。函數
let values = [1, 2, 3]; for (let num of values) { console.log(num); }
例如這段簡單的代碼,實際上調用了values上的iterator的next方法,將result上的value拿出來賦給num。既然是這樣咱們能夠採用這樣的方法來得到默認的iterator。ui
let values = [1, 2, 3]; let iterator = values[Symbol.iterator]();
在ES6中對於集合類型的Object,其上定義了一些內置的iterator,分別是;code
entries() - 返回一個返回key-value pair的iterator對象
values() - 返回一個返回collection對應值的iterator // chrome not supported
MDN接口
keys() - 返回一個返回collecttion對應key的iteratorip
以上就是iterator和generator的一些基本概念,下邊咱們來看一下一些高階應用。ci
上邊的例子中咱們在調用iterator的next方法都是無參數調用的,可是咱們一樣能夠向next方法中傳遞參數。例如這樣。
function* createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let i= createItreator() i.next() // {value:1 done: false} i.next(5) // {value: 7 done: false} i.next(3) // {value: 6 done: false}
咱們看上邊這個例子,在第二次調用中咱們傳進去了5,返回值是7,這個傳進去的參數能夠理解爲上一次yield的返回值。注意yield自己是不返回任何值的,它只向外部產生值。若是咱們查看yield在英語詞典中的意思,produce or generate (a result, gain, or financial return
因此yield的值是向外產生值。因此在第一次next後 first的值依舊是undefined。可是向next中傳遞參數,這個參數表明咱們想要上一次yield在generator函數中的值。因此在第二次next後 返回值的value就是7(5+2)了。第三例子同理。因此基於上邊的緣由咱們向第一個next函數中傳入任何值都是沒有意義的。咱們變化一下再看
function* createIterator() { yield 1; let first; let second = yield first + 2; yield second + 3; } i.next() // {value:1 done: false} i.next(5) // {value: NaN done: false} i.next(3) // {value: 6 done: false}
在第二個next中咱們的返回是NaN, 爲何呢?這是由於first是Undefined,第一次的yield並無給first賦值。因此在yeild中的執行順序是每一次執行到相應的yield就完了,下次繼續向下執行。
在iterator中咱們能夠來throw error 來達到控制執行的目的。例如上邊一個例子。
function* createIterator() { let first = yield 1; let second = yield first + 2; yield second + 3; } let i= createItreator() i.next() // {value:1 done: false} i.next(5) // {value: 7 done: false} i.throw(new Error('error')) // error thrown done is set to true after throw error
一樣在generator 咱們可使用 return來返回。
function* createIterator() { yield 1; return; yield 2; yield 3; } let iterator = createIterator(); console.log(iterator.next()); // "{ value: 1, done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
第一次next後已經結束了因此 咱們第二次next後done就已是true了。
咱們可使用generator和Iterator來實現一個task runner,可讓咱們不用手動的next,而是一次執行結束。代碼以下:
function run(taskDef) { let task = taskDef(); let value = task.next() function step() { if (!value.done) { value = task.next(value.value) step() } } step() } run(function*(){ let first = yield 1; let second = yield first + 3; yield second + 4; })
上邊就是一個例子,這樣定義的run function就能夠順序執行這些generator定義的步驟。
實際上generator和Iterator最爲實際的做用是能夠控制異步函數的執行,下邊咱們能夠簡單的例子。
function run(taskDef) { let task = taskDef(); let result = task.next() function step() { if (!result.done) { if (typeof result.value === "function") { result.value(function(err, data) { if (err) { console.log('err', err); task.throw(err) return } console.log('err', data); result = task.next(data); step() }) } else { result = task.next(result.value) step() } } } step() } let fs = require("fs"); function readFile(filename) { return function (callback) { fs.readFile(filename, callback); }; } run(function* () { let contents = yield readFile("abc.json"); console.log(contents); console.log("Done"); });
首先咱們定義了一個task runner run function
在其中當發現result中的value是function的時候,就執行這個function, 而且在異步函數的callback中,當沒有error的時候執行下一步。
在看咱們的ReadFile function,fs模塊中的readFile是一個異步的函數,而在這裏咱們將其進行了封裝成爲一個新的函數。讓其返回一個function給在task runner中使用。那麼在咱們的generator函數中,咱們看上去的代碼就和同步的同樣了,先readfile,完成後將其輸出。這樣使用Iterator和generator能夠幫助咱們寫出一個比較好看的異步執行函數。