在Javascript中,你們討論的最多的就是異步編程的操做,如何避免回調的屢次嵌套。異步操做的回調一旦嵌套不少,不只代碼會變的臃腫,還很容易出錯。各類各樣的異步編程解決方案也被不斷提出,例如你們所熟知的Promise,co等等。今天所講的Generator和yield就是和異步編程有關,能夠幫助咱們把異步編程同步化。html
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。異步
在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); } //你 //的 //名字
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 對象和原始類型的值(數值、字符串和布爾值,但這時等同於同步操做)。