據說 ES6 的 Generator 是一個很神奇的函數,因此去了解了一下。 由於它不一樣於以往的尋常函數,可是帶來的體驗卻很是好 。這裏首先講了 Generator 是什麼,分割線後面用了一個例子來講明 Generator 到底好在哪裏 ~ (能夠選擇性閱讀~ )javascript
Generator 是一種異步編程解決方案,不明白?往下看 ~java
Generator 究竟是什麼?官方文檔是這樣說的:「Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.」ajax
意思就是 Generator 函數內部的執行是能夠暫停的。你可以執行到一半退出函數,同時執行到當前的上下文環境(包括變量等等)所有被保存下來,若是咱們稍後又進來繼續執行下一段,此時是在上次狀態的基礎上繼續往下執行。感受和別的函數很不同,如何去理解呢?編程
直接上代碼:json
var myGen = function*(){ // 定義一個 Generator 函數 var one = yield 1; var two = yield 2; var three = yield 3; console.log(one,two,three); } var gen = myGen(); console.log(gen.next()); //第一次執行函數,輸出 {value:1,done:false} console.log(gen.next()); //第二次執行函數,輸出 {value:2,done:false} console.log(gen.next()); //第三次執行函數,輸出 {value:3,done:false} console.log(gen.next()); //第四次執行函數,輸出{value:undefined,done:true}
能夠看到 function 後面加了一個 * 號,那是特有的 Generator 的寫法,function* 就表示我定義了一個 Generator function。內部還有三個 yield 。異步
那麼上面那段函數是如何執行的呢,一步一步來:異步編程
定義一個 Generator 函數 —— var myGen = function*(){…} ;函數
var gen = myGen() 進行賦值,接下來想要執行函數須要調用 next(),由於 Generator 函數不會本身執行,next() 能夠 return 一個對象,包含兩個屬性,一個是輸出的值(yielded value),還有一個是 done property,表示這個 Generator 函數是否已經輸出了最後一個值(yielded its last value)了;url
調用 next() 後,執行函數內部第一條語句 var one = yield 1,因爲這條函數執行順序是自右向左,因此先執行 yield 1,yield 1 這裏的做用相似 return 1,返回一個 1 ,同時暫停了函數的執行,它後面的語句並不會繼續執行下去。因此第一句 console.log(gen.next()) 輸出 1。code
以此類推,最後的 console.log(gen.next()) 結果爲 {value:undefined,done:true},由於函數已經沒有任何 yield,沒有 return 任何值回來,done 爲 true 表示這個 Generator 函數執行結束了。
myGen 函數內的 console.log(one,two,three) 會輸出什麼呢? 1,2,3 ?猜錯了!其實這裏輸出的是 undefined,undefined,undefined。由於 yield 在 var one = 和 1 中間,因此 1 並無賦值到變量 one 上面。
下面加一點好玩的東西:把上面全部的 console.log() 改爲下面這個樣子。
console.log(gen.next()); console.log(gen.next(4)); console.log(gen.next(5)); console.log(gen.next('a'));
這時, console.log(one,two,three) 輸出的就是 4,5,a。
WHY?? 剛剛說到 var one = yield 1;的執行順序是自右向左的,執行到 yield 1就暫停了,因此並無給 one 賦值,因此執行 console.log(gen.next(4)) 語句的時候,把參數 4 賦值給了 one ,而後繼續執行 yield 2,以此類推...
然而這看起來好像並無什麼卵用?別急,繼續往下看 ~
———————————我是分割線—————————————
首先咱們知道日常常常會遇到這樣一種狀況,就是須要寫嵌套的 AJAX,特別是比較複雜的項目裏面,AJAX 一層層嵌套( 會存在多層回調嵌套,叫作 Pyramid of Doom),體驗很是糟糕。
好比下面這樣子的:
$.ajax({ // 第一個AJAX type:'GET', url:'booklist.json', success:function(booklist){ console.log(booklist); $.ajax({ // 第二個AJAX type:'GET', url:'book.json?id='+booklist.id, success:function(book){ console.log(book); $.ajax({ //第三個AJAX type:'GET', url:'comments.json?id='+book[0].id, success:function(comments){ console.log(comments); }, error:function(xhr,status,error){ //處理一些東西 } }); }, error:function(xhr,status,error){ //處理一些東西 } }); }, error:function(xhr,status,error){ //處理一些東西 } })
三個AJAX嵌套在一塊兒,雖然被簡化了(一些數據處理直接用 console.log() 替代),可是仍是看起來比較亂,一亂起來中午吃個飯回來都找不到上次寫到哪裏了,然而實際業務中要作的處理也絕對比這多不少。
這裏先用一種比較普通的方式來處理上面那大段代碼:
$.ajax({ type:'GET', url:'booklist.json', success:getbook, error:handleError }); function getbook(booklist){ console.log(booklist); $.ajax({ type:'GET', url:'book.json?id='+booklist.id, success:getComments, error:handleError }); } function getComments(book){ console.log(book); $.ajax({ type:'GET', url:'comments.json?id='+book[0].id, success:function(comments){ console.log(comments); }, error:handleError }); } function handleError(xhr,status,error){ //處理一些東西 }
上面的代碼封裝了三個 function,看起來整齊一些了,而且全部的 error 處理都經過調用本身封裝好的 handleError 去作,然而咱們的代碼量並無減小多少。
下面用 Promise 對代碼進行處理:
$.get(booklist.json).then(function(booklist){ console.log(booklist); return $.get('book.json?id='+booklist.id); }).then(function(book){ console.log(book); return $.get('comments.json?id='+book[0].id) }).then(function(comments){ console.log(comments); },handleError); function handleError(xhr,status,error){ //處理一些東西 }
Promise 經過 .then() 將函數串聯在一塊兒,它採用了同步的方式去處理異步的問題,去除了一層層的回調嵌套,error 處理只須要最後調用一次 handleError 就夠了,彷佛已經很好了,不過這裏還有更好的方法——Generator。
下面是用 Generator 處理:
Promise.coroutine(function*(){ var booklist = yield $.get('booklist.json'); console.log(booklist); var book = yield $.get('book.json?id='+booklist.id); console.log(book); var comments = yield $.get('comments.json?id='+book[0].id); console.log(book); })().catch(function(errs){ //處理一些東西 })
對比第一段代碼,瞬間神清氣爽了!Promise.coroutine 是專門用來給 Generator 用的,做用就至關於最開始的那個帶上參數的 next()。咱們不須要去把回調函數嵌套在一塊兒,或者串聯在一塊兒就可以獲得想要的結果,是否是很是贊~?!!ꉂ ೭(˵¯̴͒ꇴ¯̴͒˵)౨」
References: