Javascript中的Generator函數和yield關鍵字

在Javascript中,你們討論的最多的就是異步編程的操做,如何避免回調的屢次嵌套。異步操做的回調一旦嵌套不少,不只代碼會變的臃腫,還很容易出錯。各類各樣的異步編程解決方案也被不斷提出,例如你們所熟知的Promise,co等等。今天所講的Generator和yield就是和異步編程有關,能夠幫助咱們把異步編程同步化。html

Generator簡介

Generator在形式上和函數差很少,只是在function和函數名之間多了一個*。Generator內部必須使用yield關鍵字。例如:編程

function * gen(){
  var result1 = yield 'hello';
  var result2 = yield 'world';
  return result1 + result2;
}

當調用Generator函數時,並不會執行函數內部的代碼,而是返回一個遍歷器,該遍歷器包含一個next方法。每次執行next方法,Generator函數體會開始執行,直到遇到yield語句,執行該語句並在此暫停。用法以下:數組

var g = gen();
g.next(1);
//{value : 'hello', done : false}
g.next(2);
//{value : 'world', done : false}
g.next();
//{value : 3, done: true}
g.next();
//{value : undefined, done: true}

調用next方法會返回一個對象,這個對象包含兩個屬性,value和done,value便是當前yield語句的值。done表示Generator函數體是否被執行完。next方法同時接受一個參數,這個參數會做爲yield語句的返回值,能夠被後面的程序所使用。當程序執行完或者遇到return語句,value即爲函數體的返回值,done就變成了true。至此,若是再執行next方法,value就爲undefined, done依然是true。異步

Generator在遍歷中的應用

在js中,咱們要遍歷一個數組,咱們能夠用for...of這樣的語句來進行遍歷,這其實也是由於數組中包含了一個Generator遍歷器。若是咱們的本身定義的對象也包含一個遍歷器,咱們也就能夠經過for...of等遍歷語句來遍歷自定義的對象。這個遍歷器被存在Symbol.iterator屬性中。async

var myArray = {
  0: '你',
  1: '的',
  2: '名字',
  length: 3
};

myArray[Symbol.iterator] = function * (){
  for(var i = 0; i < this.length; i++) {
    yield this[i];
  }
};

for(var item of myArray) {
  console.log(item);
}
//你
//的
//名字

Generator在異步編程中的應用

Javascript的核心就是異步編程,每一個異步操做都會提供一個callback回調函數來返回執行的結果。假設咱們有幾個操做,後一個操做依賴前一個操做的結果,若是採用回調的方式:異步編程

step1(function(value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3)) {
      //some code
    }
  });
})

這樣的代碼一單回調的嵌套變多,會讓程序變的很是難理解,同時也很容易出錯。咱們要作的就是將回調變的扁平化。Promise對象就是這樣的功能,將上述的操做Promise化:函數

step1().then(function(value1){
  return step2(value1);
}).then(function(value2){
  return step3(value2);
}).then(function(){
  //some code
})

咱們能夠看到嵌套變少了,可是這並非最理想的解決方案,若是咱們能將異步操做變成同步操做那樣,即沒了嵌套,程序也會變的好理解。Generator函數就給咱們提供的這樣的機會。this

function *workflow(){
  var value1 = yield step1();
  var value2 = yield step2();
  var value3 = yield step3();
  //some code
}

這樣就是咱們但願結果,異步編程編程了同步編程的形式。咱們接下來要作的是讓這個Generator執行起來,因此咱們須要一個執行器。co就是一個執行器,讓Generator自動執行。code

co(function *workflow(){
  var value1 = yield step1();
  var value2 = yield step2();
  var value3 = yield step3();
  //some code
});

co有個限制,yield語句後面跟的只能是Promise對象或者Thunk函數,關於co更詳細的介紹,能夠參考阮老師的文章co 函數庫的含義和用法。然而這樣的方法依然須要依賴外在的庫函數,因而ES6中提出了async和await關鍵字。async和await其實就是Generator的語法糖。只是它自帶執行器。將上面的代碼改寫成async形式:htm

async function workflow(){
  var value1 = await step1();
  var value2 = await step2();
  var value3 = await step3();
  //some code
}

var result = workflow();

async沒有了co的限制。await關鍵字後面能夠跟 Promise 對象和原始類型的值(數值、字符串和布爾值,但這時等同於同步操做)。

相關文章
相關標籤/搜索