ES6 Generator 函數

簡介

Generator函數是ES6提供的一種異步編程解決方案,最大的特色就是 : 能夠交出函數的執行權 (即暫停執行)。編程

形式上 , Generator函數是一個普通函數 , 可是有兩個特徵 :異步

  1. function 關鍵字與函數名之間有一個 ( * )。
  2. 函數內部使用 yield 語句, 定義不一樣的內部狀態。

定義及調用

  • 函數聲明

ES6沒有規定,function關鍵字與函數名之間的星號,寫在哪一個位置。這致使下面的寫法都能經過。異步編程

function * foo(x, y) { ··· }

     function *foo(x, y) { ··· }

     function* foo(x, y) { ··· }

     function*foo(x, y) { ··· }
複製代碼
  • 函數表達式函數

    let foo = function * (){ ... }
    複製代碼
  • 函數調用spa

Generator 函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用Generator函數後,該函數並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象===》遍歷器對象(Iterator Object)。prototype

下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用next方法,內部指針就從"函數頭部或上一次停下來的地方"開始執行,直到遇到下一個yield語句(或return語句)爲止。指針

function * fn(){
            yield "one";
            yield "two";
            return "last";
     }
     
       let e = fn();
       console.log(e.next()); // {value: "one", done: false}
       console.log(e.next()); // {value: "two", done: false}
       console.log(e.next()); // {value: "last", done: true}
       console.log(e.next()); // {value: undefined, done: true}
複製代碼

value屬性表示當前的內部狀態的值,是yield語句後面那個表達式的值;done屬性是一個布爾值,表示是否遍歷結束。code

yield 語句

yield語句就是暫停標誌對象

遍歷器對象的next方法的運行邏輯以下:接口

  1. 遇到yield語句,就暫停執行後面的操做,並將緊跟在yield後面的那個表達式的值,做爲返回的對象的value屬性值。
  2. 下一次調用next方法時,再繼續往下執行,直到遇到下一個yield語句。
  3. 若是沒有再遇到新的yield語句,就一直運行到函數結束,直到return語句爲止,並將return語句後面的表達式的值,做爲返回的對象的value屬性值。
  4. 若是該函數沒有return語句,則返回的對象的value屬性值爲undefined。

注意 :

  • Generator函數能夠不用yield語句,這時就變成了一個單純的暫緩執行函數。

    function * fn(){
               console.log("hahaha")
       }
        let generator = fn();
        setTimeout(function(){
             generator.next()
        },2000)
    複製代碼
  • yield語句不能用在普通函數中,不然會報錯。

next方法的參數

yield句自己沒有返回值,或者說老是返回undefined。next方法能夠帶一個參數,該參數就會被看成上一個yield語句的返回值。

注意了 : 是上一個yield 語句的返回值 , 這一點在循環中比較容易搞混。

function* f() {
        for(var i=0; true; i++) {
             var reset = yield i;
             if(reset) { i = -1; }
         }
     }

     var g = f();

     g.next() // { value: 0, done: false }
     g.next() // { value: 1, done: false }
     g.next(true) // { value: 0, done: false }
     // 第三步的true會當作上一個yield語句的返回值 , 也就是說 i會先賦值爲 -1 ,而後 i++ 變爲 0 輸出。
複製代碼

for ... of 循環

for...of循環能夠自動遍歷Generator函數時生成的Iterator對象,且此時再也不須要調用next方法。

function *foo() {
             yield 1;
             yield 2;
             yield 3;
             yield 4;
             yield 5;
             return 6;
     }

     for (let v of foo()) {
             console.log(v);
     }
     // 1 2 3 4 5
複製代碼

一旦next方法的返回對象的done屬性爲true,for...of循環就會停止,且不包含該返回對象,因此上面代碼的return語句返回的6,不包括在for...of循環之中。可是 , 若將 6 換爲yield 則會輸出 , 由於此時 done 爲 false。

除了for...of循環之外,擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味着,它們均可以將Generator函數返回的Iterator對象,做爲參數。

// 擴展運算符
     
     [...fn()] // [1,2,3,4,5]
     
     // Array.from() 方法
     
     Array.from(fn()) // [1,2,3,4,5]
     
     // 解構賦值
     
     let [x,y,z] = fn()
     // x 1 y 2 z3
複製代碼

Generator.prototype.throw()

Generator函數返回的遍歷器對象,都有一個throw方法,能夠在函數體外拋出錯誤,而後在Generator函數體內捕獲。

function * fn(){
           try {
               yield
           }catch(e){
               console.log('內部捕獲',e.message)
     }
     }
     var i = fn()
     i.next() 
     try{
        i.throw(new Error('你說你錯了'))
        i.throw('不走心那')
     }catch(e){
         console.log("外部捕獲",e)
     }
     
     // 內部捕獲 你說你錯了
     // 外部捕獲 不走心那
複製代碼

Generator.prototype.return()

Generator函數返回的遍歷器對象,還有一個return方法,能夠返回給定的值,而且終結遍歷Generator函數。

function * fn(){
         yield 1;
         yield 2;
         yield 3;
     }
      let g = fn();

      console.log(g.next())  // {value: 1, done: false}
      console.log(g.return(10)) //{value: 10, done: true}
      console.log(g.next()) // {value: undefined, done: true}
複製代碼

若是return方法調用時,不提供參數,則返回值的value屬性爲undefined。

yield*語句

若是在Generater函數內部,調用另外一個Generator函數,默認狀況下是沒有效果的。 這個就須要用到yield*語句,用來在一個Generator函數裏面執行另外一個Generator函數。

function* foo() {
            yield 'a';
            yield 'b';
     }

     function* bar() {
            yield 'x';
            foo();
            yield 'y';
     }
     function* bar() {
         yield 'x';
         yield* foo();
         yield 'y';
     }
     
     // "x"
     // "a"
     // "b"
     // "y"
複製代碼
相關文章
相關標籤/搜索