從iterator到generator

iterator

可遍歷(可迭代)協議

一個對象爲了變成可遍歷對象,好比說能夠用 for ... in 結構遍歷其屬性值,必須實現 @@iterator 方法, 意思是這個對象(或者它原型鏈 prototype chain 上的某個對象)必須有一個名字是 Symbol.iterator 的屬性。閉包

屬性
[Symbol.iterator] 返回一個對象的無參函數,被返回對象符合可遍歷協議。

迭代器協議

當一個對象被認爲是一個迭代器時,它實現了一個 next() 的方法。
該方法返回一個對象包含 donevalue 屬性,done 的值表示迭代器是否能夠產生序列中的下一個值,value 爲迭代器返回的任何 JavaScript 值。donetrue 時可省略。異步


let a = {
    q: 'q',
    w: 'w',
    e: 'e',
};
Object.defineProperty(a, length, {
    enumerable: false,
    value: 3
});

如今對 a 嘗試用 for ... in 結構遍歷其屬性值async

for (let v of a) {
    console.log(v);
}

報錯:函數

Uncaught TypeError: a[Symbol.iterator] is not a function

定義一個函數利用閉包實現一個將 a 轉變爲可迭代對象工具

function Iterator(obj) {
    let i;

    return ()=> {
        return {
            next: ()=> {
                if (i < obj.length) {
                    for (i in obj) {
                        return {
                            done: false,
                            value: obj[i]
                        };
                    }
                }
                return {
                    done: true
                };
            }
        };
    };
}

a[Symbol.iterator] = Iterator(a);

generator

Generator 函數最大特色就是能夠交出函數的執行權(即暫停執行)。異步操做須要暫停的地方,都用 yield 語句註明。調用 Generator 函數並不會執行本體,而是每次調用 next 方法的時候,執行到下一個碰到的 yield 處。ui

Generator 函數的數據交換

function* anotherGenerator(i) {
  yield i + 1;
  let x = yield i + 2;
  yield x + 3;
}

function* generator(i){
  yield i;
  yield* anotherGenerator(i);
  // 執行權轉交給另外一個 generator 函數的話 yield 後面帶星號,直接調用的話是沒有效果的
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next(2).value); // 5
console.log(gen.next().value); // 20
// next 的返回值和迭代器的 next 相似,yield 語句的執行結果做爲 value,是否還有下一個 yield 決定 done
// next 方法能夠帶有參數,這個參數能夠傳入 Generator 函數,做爲上個階段異步任務的返回結果

Generator 函數的錯誤處理

Generator 函數內部還能夠部署錯誤處理代碼,捕獲函數體外拋出的錯誤。prototype

function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){ 
    console.log(e);
  }
  return y;
}

var g = gen(1);
g.next();
g.throw('出錯了');
// 出錯了

上面代碼的最後一行,Generator 函數體外,使用指針對象的 throw 方法拋出的錯誤,能夠被函數體內的 try ... catch指針

Generator 函數的終止

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

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }

若是Generator函數內部有try...finally代碼塊,那麼return方法會推遲到finally代碼塊執行完再執行。對象

function* numbers () {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers()
g.next() // { done: false, value: 1 }
g.next() // { done: false, value: 2 }
g.return(7) // { done: false, value: 4 }
g.next() // { done: false, value: 5 }
g.next() // { done: true, value: 7 }

上面代碼中,調用return方法後,就開始執行finally代碼塊,而後等到finally代碼塊執行完,再執行return方法。

Generator 函數的自動執行

co 庫是tj寫的一個讓Generator函數自動執行的工具。

let co = require('co');
let p = co(gen);

p.then(function (){
  console.log('ok');
})

co 函數返回一個 Promise 對象,所以能夠用 then 方法添加回調函數。

async 與 await

ES7 爲 generator 的語法糖

相關文章
相關標籤/搜索