一、什麼是 Generator 函數ajax
在Javascript中,一個函數一旦開始執行,就會運行到最後或遇到return時結束,運行期間不會有其它代碼可以打斷它,也不能從外部再傳入值到函數體內express
而Generator函數(生成器)的出現使得打破函數的完整運行成爲了可能,其語法行爲與傳統函數徹底不一樣編程
{ // 傳統函數 function foo() { return 'hello world' } foo() // 'hello world',一旦調用當即執行 // Generator函數 function* generator() { yield 'status one' // yield 表達式是暫停執行的標記 return 'hello world' } let iterator = generator() // 調用 Generator函數,函數並無執行,返回的是一個Iterator對象 iterator.next() // {value: "status one", done: false},value 表示返回值,done 表示遍歷尚未結束 iterator.next() // {value: "hello world", done: true},value 表示返回值,done 表示遍歷結束 }
{ function* gen() { yield 'hello' yield 'world' return 'ending' } let it = gen() it.next() // {value: "hello", done: false} it.next() // {value: "world", done: false} it.next() // {value: "ending", done: true} it.next() // {value: undefined, done: true} }
{ (function (){ yield 1; })() // SyntaxError: Unexpected number // 在一個普通函數中使用yield表達式,結果產生一個句法錯誤 }
{ function* demo() { console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); // OK } }
{ function* demo() { foo(yield 'a', yield 'b'); // OK let input = yield; // OK } }
{ function* foo() { yield 'aaa' yield 'bbb' } function* bar() { foo() yield 'ccc' yield 'ddd' } let iterator = bar() for(let value of iterator) { console.log(value) } // ccc // ddd }
{ function* foo() { yield 'aaa' yield 'bbb' } function* bar() { yield* foo() // 在bar函數中 **執行** foo函數 yield 'ccc' yield 'ddd' } let iterator = bar() for(let value of iterator) { console.log(value) } // aaa // bbb // ccc // ddd }
[rv] = yield [expression]
expression:定義經過遍歷器從生成器函數返回的值,若是省略,則返回 undefined
rv:接收從下一個 next() 方法傳遞來的參數
{ function* gen() { let result = yield 3 + 5 + 6 console.log(result) yield result } let it = gen() console.log(it.next()) // {value: 14, done: false} console.log(it.next()) // undefined {value: undefined, done: false} }
{ function* gen() { let result = yield 3 + 5 + 6 console.log(result) yield result } let it = gen() console.log(it.next()) // {value: 14, done: false} console.log(it.next(3)) // 3 {value: 3, done: false} }
{ function* gen() { let result = yield 3 + 5 + 6 console.log(result) yield result } let it = gen() console.log(it.next(10)) // {value: 14, done: false} console.log(it.next(3)) // 3 {value: 3, done: false} }
{ function* gen(x) { let y = 2 * (yield (x + 1)) // 注意:yield 表達式若是用在另外一個表達式中,必須放在圓括號裏面 let z = yield (y / 3) return x + y + z } let it = gen(5) /* 經過前面的介紹就知道這部分輸出結果是錯誤的啦 console.log(it.next()) // {value: 6, done: false} console.log(it.next()) // {value: 2, done: false} console.log(it.next()) // {value: 13, done: false} */ /*** 正確的結果在這裏 ***/ console.log(it.next()) // 首次調用next,函數只會執行到 「yield(5+1)」 暫停,並返回 {value: 6, done: false} console.log(it.next()) // 第二次調用next,沒有傳遞參數,因此 y的值是undefined,那麼 y/3 固然是一個NaN,因此應該返回 {value: NaN, done: false} console.log(it.next()) // 一樣的道理,z也是undefined,6 + undefined + undefined = NaN,返回 {value: NaN, done: true} }
{ function* gen(x) { let y = 2 * (yield (x + 1)) // 注意:yield 表達式若是用在另外一個表達式中,必須放在圓括號裏面 let z = yield (y / 3) return x + y + z } let it = gen(5) console.log(it.next()) // 正常的運算應該是先執行圓括號內的計算,再去乘以2,因爲圓括號內被 yield 返回 5 + 1 的結果並暫停,因此返回{value: 6, done: false} console.log(it.next(9)) // 上次是在圓括號內部暫停的,因此第二次調用 next方法應該從圓括號裏面開始,就變成了 let y = 2 * (9),y被賦值爲18,因此第二次返回的應該是 18/3的結果 {value: 6, done: false} console.log(it.next(2)) // 參數2被賦值給了 z,最終 x + y + z = 5 + 18 + 2 = 25,返回 {value: 25, done: true} }
{ function* gen(x) { let y = 2 * (yield (x + 1)) let z = yield (y / 3) z = 88 // 注意看這裏 return x + y + z } let it = gen(5) console.log(it.next()) // {value: 6, done: false} console.log(it.next(9)) // {value: 6, done: false} console.log(it.next(2)) // 這裏其實也很容易理解,參數2被賦值給了 z,可是函數體內又給 z 從新賦值爲88, 最終 x + y + z = 5 + 18 + 88 = 111,返回 {value: 111, done: true} }
五、與 Iterator 接口的關係json
{ let obj = {} function* gen() { yield 4 yield 5 yield 6 } obj[Symbol.iterator] = gen for(let value of obj) { console.log(value) } // 4 // 5 // 6 console.log([...obj]) // [4, 5, 6] }
{ function* gen() { yield 1 yield 2 yield 3 yield 4 return 5 } for(let item of gen()) { console.log(item) } // 1 2 3 4 }
{ function* gen() { yield 1 yield 2 yield 3 } let it = gen() it.next() // {value: 1, done: false} it.return('ending') // {value: "ending", done: true} it.next() // {value: undefined, done: true} }
{ let count = 6 // 聲明一個全局變量 // 具體抽獎邏輯的方法 function draw() { // 執行一段抽獎邏輯 // ... // 執行完畢 console.log(`剩餘${count}次`) } // 執行抽獎的方法 function startDrawing(){ if(count > 0) { count-- draw(count) } } let btn = document.createElement('button') btn.id = 'start' btn.textContent = '開始抽獎' document.body.appendChild(btn) document.getElementById('start').addEventListener('click', function(){ startDrawing() }, false) }
{ // 具體抽獎邏輯的方法 function draw(count) { // 執行一段抽獎邏輯 // ... console.log(`剩餘${count}次`) } // 執行抽獎的方法 function* remain(count) { while(count > 0) { count-- yield draw(count) } } let startDrawing = remain(6) let btn = document.createElement('button') btn.id = 'start' btn.textContent = '開始抽獎' document.body.appendChild(btn) document.getElementById('start').addEventListener('click', function(){ startDrawing.next() }, false) }
{ // 請求的方法 function* ajax() { yield new Promise((resolve, reject) => { // 此處用一個定時器來模擬請求數據的耗時,並約定當返回的json中code爲0表示有新數據更新 setTimeout(() => { resolve({code: 0}) }, 200) }) } // 長輪詢的方法 function update() { let promise = ajax().next().value // 返回的對象的value屬性是一個 Promise 實例對象 promise.then(res => { if(res.code != 0) { setTimeout(() => { console.log('2秒後繼續查詢.....') update() }, 2000) } else{ console.log(res) } }) } update() }