終於開始寫generator了,離這個系列的終結又進了一步。其實generator我還處在會用可是不理解原理的狀態,可是知識不總結,不記錄的話容易忘記,因此我仍是把如今的一點心得記錄下來。等到之後有了更深的理解再回來補充。
想要看更深度解析generator的朋友能夠移步漫話JavaScript與異步·第三話——Generator:化異步爲同步這裏面談及了generator的底層實現及generator的用法。是我看過的文章中自認爲解釋的最好的一篇,並且篇幅也不長,建議你們去看一看。html
根據一向的做風,咱們先嚐試本身實現generator
嘗試ing............
好了嘗試完了,實現不了,老老實實的學習generator的用法吧。es6
在個人理解中,generator最大的特色就是可讓函數在特定的地方停下,等待被喚醒後在函數內部環境中繼續執行。咱們結合代碼來看一看:
註釋:【1】Iterator Object對象:參考 Iterator 文章比較長,可是若是隻是想要了解什麼是Iterator Object的話看完第一小節就足夠了promise
//輸出分割線的函數,感興趣的能夠自行百度如何設置console.log的樣式 function cut_off(color) { console.log("%c------------------------------------------","color:"+color+";font-size:20px"); } //* 爲generator函數的標識,若是咱們想要建立一個generator函數就必須在function後面加上* function* generator() { let num1, num2; num1 = 123; console.log("num1", num1, "num2", num2); //yield就是該函數內部暫停的地方,暫停的同時會把yield後面的值返回出去 yield num1; num2 = 456; console.log("num1", num1, "num2", num2); yield num2; console.log("num1", num1, "num2", num2); return "end" } console.log("generator defined"); //函數返回一個Iterator Object對象; // 可是與普通函數不一樣的是,這個時候函數並不執行函數內部的代碼 let g = generator(); console.log("g defined"); cut_off("red"); console.log("g.next() run 1"); //開始執行函數內部的代碼,而且遇在到yield的時候返回 yield後面的值 console.log(g.next()); cut_off("red"); console.log("g.next() run 2"); //從上次執行完的地方執行,而且遇在到yield的時候返回 yield後面的值 console.log(g.next()); cut_off("red"); console.log("g.next() run 3"); //從上次執行完的地方執行,此次是最後一次有值的返回,done的狀態會變爲true console.log(g.next()); cut_off("red"); console.log("g.next() run 4"); //已經執行完成以後再次被調用,永遠返回{value:undefined, done: true} console.log(g.next()); cut_off("red"); console.log("g.next() run 5"); //已經執行完成以後再次被調用,永遠返回{value:undefined, done: true} console.log(g.next());
貼上一張代碼和運行結果的對比圖輔助你們理解
但願你們看到這裏已經理解了generator的基本用法,可是這個東西確實有點難,因此我放出聯繫方式
我有空的時候能夠一塊兒探討一下dom
接下來我要講generator最重要的第二個特性,咱們能夠經過.next(value)
爲yeild
賦值(這是不許確的說法,可是咱們能夠這麼理解,方便咱們使用generator),仍是貼代碼:異步
function* generator() { //第一次調用.next()是啓動了這個函數一直運行到下一個yield的位置 let num1, num2; num1 = 123; console.log("num1", num1, "num2", num2); num2 = yield num1;/*1.因爲運算的順序,js會先計算右邊的值,也就是在num2被賦值以前,函數就中止運行了 2.第二次調用.next(value)的時候,value被next傳入了yield的位置 3.程序繼續運行,value被賦值給了num2 */ console.log("num1", num1, "num2", num2); return num2 } let g = generator(); console.log("g.next() run 1"); console.log(g.next()); cut_off("red"); console.log("g.next(789) run 2"); //從上次執行完的地方執行,而且將789傳入函數內部 console.log(g.next(789));
貼出函數和運行結果的對比圖,輔助你們理解.next(value)
爲yeild賦值(該說法並不許確)
generator的基礎學習到這裏就能嘗試實際使用了,接下來咱們嘗試讓generator在異步中大展身手吧,話很少說上代碼;函數
//模擬異步請求 let request = function (sucF, errF) { if ((sucF && typeof sucF !== "function") || (errF && typeof errF !== "function")) { throw new Error("傳入參數必須爲函數") } setTimeout(function () { let data = parseInt(Math.random() * 100); if (data < 90 && sucF) { sucF(data) } else if (errF) { errF("本次異步失敗了") } }, 100) }; function* generator() { let num1, num2; num1 = yield request((data) => { g.next(data) //這個地方可能有些難以理解,爲何在g被建立以前,就被使用了。 }, err => { //這個問題能夠看註釋【1】來輔助理解 console.error(err); g.return(err) //跳過全部的yield,return直接返回err }); console.log("num1", num1, "num2", num2); num2 = yield request((data) => { g.next(data) }, err => { console.error(err); g.return(err) //跳過全部的yield,return直接返回err }); console.log("num1", num1, "num2", num2); return num2 } let g = generator(); //啓動 g.next();
註釋【1】:爲了解釋g爲何能夠在被建立以前就‘被調用’學習
//其實在運行generator的時候generator內部的代碼塊並無開始運行,而是返回了一個Iterator對象,因此你們能夠這樣簡單理解 //function* generator 至關於 function generator(){ let obj = {}; obj.next=function(){ console.log(g); }; return obj } //這樣在調用generator()的時候,g不會被使用 let g = generator(); //這個時候g被調用了,可是在上一行代碼中,g已經被定義了,因此沒有問題,不會報錯 g.next()
這篇文章暫時寫到這裏,由於個人頁面開始卡了,因此我把generator和promise結合的用法放到下一篇文章中去;(ps:今天就會寫出來的)spa