Generator 函數的定義
- 語法上,可以把理解成,Generator 函數是一個狀態機,封裝了多個內部狀態。
- 形式上,Generator 函數是一個普通函數。它不同於普通函數,是可以暫停執行的,所以函數名之前要加星號,以示區別。
- 整個Generator函數就是一個封裝的異步任務,或者說是異步任務的容器,異步操作需要暫停的地方,都用yield語句。
什么是Generator函數
- function 關鍵字和函數之間有一個星號(*),且內部使用yield表達式,定義不同的內部狀態。
- 調用Generator函數后,該函數並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象。
function fn(){ // 定義一個Generator函數 yield ‘hello’; yield ‘world’; return ‘end’; } var f1 =fn(); // 調用Generator函數 console.log(f1); // fn {[[GeneratorStatus]]: “suspended”} console.log(f1.next()); // {value: “hello”, done: false} console.log(f1.next()); // {value: “world”, done: false} console.log(f1.next()); // {value: “end”, done: true} console.log(f1.next()); // {value: undefined, done: true}
但是,調用Generator函數后,函數並不執行,返回的也不是函數執行后的結果,而是一個指向內部狀態的指針對象。
下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。即:每次調用next方法,內部指針就從函數頭部或上一次停下來的地方開始執行,直到遇到下一個yield表達式(或return語句)為止。
Generator 函數是分段執行的,yield表達式是暫停執行的標記,而next方法可以恢復執行。
如下案例:
function* gen(x){//一個 Generator 函數 console.log('x='+x) var y = yield x + 2; return y; } //調用Generator 函數 var g = gen(1); g.next(); 輸出:x=1 {value: 3, done: false} 注意:調用g.next() 即執行異步任務的 x+2
- next 方法的作用是分階段執行 Generator 函數。每次調用 next 方法,會返回一個對象,表示當前階段的信息( value 屬性和 done 屬性)。
- value 屬性是 yield 語句后面表達式的值,表示當前階段的值;
- done 屬性是一個布爾值,表示 Generator 函數是否執行完畢,即是否還有下一個階段(done為false 繼續執行)。
Generator 函數的數據交換
function* gen(x){ var y = yield x + 2; return y; } var g = gen(1); //第一次執行 g.next() // { value: 3, done: false } //第二次執行 時,如果有帶參數, 這個參數可以傳入 Generator 函數,作為上個階段異步任務的返回結果,被函數體內的變量 y 接收。 g.next(2) // { value: 2, done: true } 因此,這一步的 value 屬性,返回的就是2(變量 y 的值)。 //如果沒帶參數 g.next() //{value: undefined, done: true}
1.迭代器協議: 定義了一種標准的方式來產生一個有限或無限序列的值;
當一個對象被認為是一個迭代器時,它實現了一個 next() 的方法,next()返回值如下:
{ done:true,//false迭代是否結束, value:v,//迭代器返回值 }
2.generator的用途:
在JavaScript中,一個函數一旦被執行,就會執行到最后或者被return,運行期間不會被外部所影響打斷,而generator的出現就打破了這種函數運行的完整性。
3.generator函數與普通函數的區別:
a.function關鍵字與函數名中間有一個*鍵 b.Generator函數使用了yield表達式 c. 直接調用 Generator函數並不會執行,也不會返回運行結果,而是返回一個遍歷器對象(Iterator Object) d.調用Generator函數時需用到next(),如果有多個yield狀態,要依次調用next() e.該生成器函數執行后會返回一個Iterator對象,對象內有yield的返回值,以及還有一個狀態done的屬性(該屬性表示當前生成器內yield表達式全部執行完畢,執行完畢返回true) { done:true,//false迭代是否結束, value:v,//迭代器返回值 }
4.generator函數的語法:
// 傳統函數 function foo() { return 'hello world' } foo() // 'hello world',一旦調用立即執行 //Generator函數 function* persition(){ yield '我是generato生成器'; yield '我要開始了'; return '結束' } //創建一個句柄,賦值給生成器 var iterator =persition(); //直接調用並不能被立即執行 console.log(iterator) //需使用next()方法來調用這個生成器 next()方法調用一次, //並不能將Generator函數內的yield值全部打印出來,需要依次進行調用 console.log(iterator.next()) console.log(iterator.next()) //如果iterator對象內done為true,證明Generator函數執行完畢 console.log(iterator.next())
5.yield表達式:
yield 表達式只能用在 Generator 函數里面,用在其它地方都會報錯 function(){ yield 1; } // SyntaxError: Unexpected number // 在一個普通函數中使用yield表達式,結果產生一個句法錯誤 }
6.next():
generator函數(生成器)調用的唯一方法,且注意需依次調用next方法,
對於普通的生成器,第一次next調用,相當於啟動生成器,會從生成器函數的第一行代碼開始執行,直到第一次執行完yield語句后,跳出生成器函數。
然后第二個next調用,進入生成器函數后,從yield語句的下一句語開始執行,然后重新運行到yield語句,執行后,跳出生成器函數,
promise generator aysnc/await
1.三者都是異步編程的解決方案,不同的是,promise為較早出來的,其次generator,最后為async/await,三者象征了前端進行解決異步編程的進化路程。
promise:
promise比較簡單,也是最常用的,主要就是將原來用 回調函數異步編程的方法 轉成 relsove和reject觸發事件;
對象內含有四個方法,then()異步請求成功后
catch()異步請求錯誤的回調方法
finally()請求之后無論是什么狀態都會執行
resolve()將現有對象轉換為Promise對象
all()此方法用於將多個Promise實例包裝成一個新的promise實例。
race()也是將多個Promise實例包裝成一個新的promise實例
reject()返回一個狀態為Rejected的新Promise實例。
有點:讓回調函數變成了規范的鏈式寫法,程序流程可以看的很清楚
缺點:編寫的難度比傳統寫法高,閱讀代碼也不是一眼可以看懂
Generator:
generator是一個迭代生成器,其返回值為迭代器(lterator),是ES6標准引入的新的數據類型,主要用於異步編程,它借鑒於Python中的generator概念和語法;
generator函數內有兩個重要方法,1 yield表達式 2.next()
Generator 函數是分段執行的,yield表達式是暫停執行的標記,而 next方法可以恢復執行
優點:1.利用循環,每調用一次,就使用一次,不占內存空間 2.打破了普通函數執行的完整性
缺點: 需要用next()方法手動調用,直接調用返回無效iterator 2.
async/await:
async:異步函數
await:同步操作
es7中提出來的異步解決方法,是目前解決異步編程終它基極解決方案,於promise為基礎,其實也就是generator的高級語法糖,本身自己就相當於一個迭代生成器(狀態機),它並不需要手動通過next()來調用自己,與普通函數一樣
async就相當於generator函數中的*,await相當於yield,
async 用於申明一個 function 是異步的,而 await 用於等待一個異步方法執行完成。
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
//await是在等待一個async函數完成
const v1 = await getSomething();
//await后面不僅可以接Promise,還可以接普通函數或者直接量
const v2 = await testAsync();
console.log(v1, v2);
}
運用場景
1、代替遞歸
斐波那契數列的實現:
function * fibonacci(seed1, seed2) { while (true) { yield (() => { seed2 = seed2 + seed1; seed1 = seed2 - seed1; return seed2; })(); } } const fib = fibonacci(0, 1); fib.next(); // {value: 1, done: false} fib.next(); // {value: 2, done: false} fib.next(); // {value: 3, done: false} fib.next(); // {value: 5, done: false} fib.next(); // {value: 8, done: false}
2、異步操作的同步化
Generator 函數的暫停執行的效果,意味着可以把異步操作寫在yield表達式里面,等到調用next方法時再往后執行。這實際上等同於不需要寫回調函數了,因為異步操作的后續操作可以放在yield表達式下面,反正要等到調用next方法時再執行。所以,Generator 函數的一個重要實際意義就是用來處理異步操作,改寫回調函數。
Ajax 是典型的異步操作,通過 Generator 函數部署 Ajax 操作,可以用同步的方式表達。
function* main() { var result = yield request(“http://some.url“); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { makeAjaxCall(url, function(response){ it.next(response); }); } var it = main(); it.next();
上面代碼的main函數,就是通過 Ajax 操作獲取數據。可以看到,除了多了一個yield,它幾乎與同步操作的寫法完全一樣。注意,makeAjaxCall函數中的next方法,必須加上response參數,因為yield表達式,本身是沒有值的,總是等於undefined。
逐行讀取文本文件:
function * numbers() { let file = new FileReader(“numbers.txt”); try { while(!file.eof) { yield parseInt(file.readLine(), 10); } } finally { file.close(); } }
3、控制流的管理
如一個多步操作非常耗時,采用回調的話:
step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // Do something with value4 }); }); }); }); 采用promise改寫: Promise.resolve(step1) .then(step2) .then(step3) .then(step4) .then(function (value4) { // Do something with value4 }, function (error) { // Handle any error from step1 through step4 }).done();
而使用generator函數:
function* longRunningTask(value1) { try { var value2 = yield step1(value1); var value3 = yield step2(value2); var value4 = yield step3(value3); var value5 = yield step4(value4); // Do something with value4 } catch (e) { // Handle any error from step1 through step4 } }
總結:
從回調函數,到promise,再到generator,再到Async/await,這四種分別代表了JavaScript異步編程解決方案的進化路程。async和genera