日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ES6必知必会 (七)—— Generator 函数

發(fā)布時間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ES6必知必会 (七)—— Generator 函数 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Generator 函數(shù)

1.Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同,通常有兩個特征:

  • function關(guān)鍵字與函數(shù)名之間有一個星號;

  • 函數(shù)體內(nèi)部使用yield表達式,定義不同的內(nèi)部狀態(tài)
    //一個簡單的 Generator 函數(shù)
    function *Generator(){
    yield 'Hello';
    yield 'World';

    return 'Hello World'; }

2.Generator 函數(shù)的調(diào)用方法與普通函數(shù)一樣,也是在函數(shù)名后面加上一對圓括號。不同的是,調(diào)用 Generator 函數(shù)后,該函數(shù)并不執(zhí)行,返回的也不是函數(shù)運行結(jié)果,而是一個指向內(nèi)部狀態(tài)的指針對象,必須調(diào)用 next 方法,才能使得指針移向下一個狀態(tài)。也就是說,每次調(diào)用next方法,內(nèi)部指針就從函數(shù)頭部或上一次停下來的地方開始執(zhí)行,直到遇到下一個yield表達式(或return語句)為止。實際上就是,Generator 函數(shù)是分段執(zhí)行的,yield表達式是暫停執(zhí)行的標記,而next方法可以恢復執(zhí)行。

function *Generator(){yield 'Hello';yield 'World'; return 'Hello World'; } let generator = Generator(); //無返回 generator.next(); //{"value":"Hello","done":false} generator.next(); //{"value":"World","done":false} generator.next(); //{"value":"Hello World","done":true} generator.next(); //{"value":undefined , "done":true}

上述代碼就是一個 Generator 函數(shù)的執(zhí)行過程 :

  • 第一次調(diào)用 next() 方法, 遇到第一個 yield 表達式后返回一個對象,對象的 vlaue 屬性值是第一個 yield 表達式的值 Hello , done屬性是 false , 表示整個遍歷還沒有結(jié)束;
  • 第二次調(diào)用 next() 方法, Generator 函數(shù)從上次yield表達式停下的地方繼續(xù)執(zhí)行 , 遇到第二個 yield 表達式后返回一個對象,對象的 vlaue 屬性值是第一個 yield 表達式的值 World , done屬性是 false , 表示整個遍歷還沒有結(jié)束;
  • 第三次調(diào)用 next() 方法, Generator 函數(shù)從上次yield表達式停下的地方繼續(xù)執(zhí)行 ,一直執(zhí)行到return語句(如果沒有return語句,就執(zhí)行到函數(shù)結(jié)束)。此時 next 方法返回的對象的value屬性,就是 return語句后面的表達式的值(如果沒有return語句,則value屬性的值為undefined),done屬性的值true,表示遍歷已經(jīng)結(jié)束;
  • 第四次調(diào)用,此時 Generator 函數(shù)已經(jīng)運行完畢,next方法返回對象的value屬性為undefined,done屬性為true。以后再調(diào)用next方法,返回的都是這個值。

3.上述例子中我們可以得知,調(diào)用 Generator 函數(shù),返回一個遍歷器對象,代表 Generator 函數(shù)的內(nèi)部指針。以后,每次調(diào)用遍歷器對象的next方法,就會返回一個有著value和done兩個屬性的對象。value屬性表示當前的內(nèi)部狀態(tài)的值,是yield表達式(或return)后面那個表達式的值;done屬性是一個布爾值,表示是否遍歷結(jié)束。

4.yield 表達式可以看做是Generator 函數(shù)的暫停標志,要注意的是 yield 表達式后面的表達式,只有當調(diào)用next方法、內(nèi)部指針指向該語句時才會執(zhí)行;

function* gen() { yield 123 + 456; }

上面代碼中,yield后面的表達式123 + 456,不會立即求值,只會在next方法將指針移到這一句時,才會求值(“惰性求值”);

5.yield 表達式與 return 語句都能返回緊跟在語句后面的那個表達式的值,不同的是遇到y(tǒng)ield,函數(shù)暫停執(zhí)行,下一次再從該位置繼續(xù)向后執(zhí)行,而 return 語句不具備位置記憶的功能,而且一個函數(shù)里面,只能執(zhí)行一個 return 語句,但是可以執(zhí)行多個yield表達式,要注意的是 yield 表達式只能用在 Generator 函數(shù)里面,用在其他地方都會報錯。

(function (){yield 1; })() // SyntaxError: Unexpected number

6.Generator 函數(shù)可以不用yield表達式,這時就變成了一個單純的暫緩執(zhí)行函數(shù)

function *Generator() { console.log('Hello World!') } var generator = Generator(); setTimeout(function () { generator.next() }, 2000); // "Hello World"

7.yield 表達式如果用在另一個表達式之中,必須放在圓括號里面,如果用作函數(shù)參數(shù)或放在賦值表達式的右邊,可以不加括號

function *Generator() { console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); // OK } function *Generator() { foo( yield 'a' , yield 'b' ); // OK let input = yield; // OK }

8.yield 表達式本身沒有返回值,或者說是返回 undefined。next 方法可以帶一個參數(shù),該參數(shù)就會被當作上一個 yield 表達式的返回值。

function *Generator() { for(var i = 0; true; i++) { var reset = yield i; if( reset ) { i = -1; } } } var g = Generator(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false }

上述代碼中,定義了一個可以無限運行的 Generator 函數(shù),如果 next 方法沒有參數(shù),每次運行到 yield 表達式,變量 reset 的值總是 undefined ,當 next 方法帶一個參數(shù)true時,變量reset就被重置為這個參數(shù)(即true),因此i會等于-1,下一輪循環(huán)就會從-1開始遞增。

我們利用這個特性,在 Generator 函數(shù)運行的不同階段,從外部向內(nèi)部注入不同的值,從而調(diào)整函數(shù)行為;

function *foo(x) { var y = 2 * (yield (x + 1)); var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // {value:6, done:false} a.next() // {value:NaN, done:false} a.next() // {value:NaN, done:true} var b = foo(5); b.next() // { value:6, done:false } b.next(12) // { value:8, done:false } b.next(13) // { value:42, done:true }

上述代碼 a 第二次運行 next 方法的時候不帶參數(shù),導致 y 的值等于2 * undefined(即NaN),除以3以后還是NaN,因此返回對象的value屬性也等于NaN。第三次運行Next方法的時候不帶參數(shù),所以z等于undefined,返回對象的value屬性等于5 + NaN + undefined,即NaN;

b 調(diào)用加了參數(shù),結(jié)果就不一樣了,第一次調(diào)用next方法時,返回 x+1 的值 6;第二次調(diào)用next方法,將上一次 yield 表達式的值設(shè)為 12 ,因此 y 等于 24,返回 y / 3 的值 8;第三次調(diào)用next方法,將上一次 yield 表達式的值設(shè)為 13 ,因此 z 等于 13 ,這時 x 等于 5,y 等于24,所以 return 語句的值等于 42;

ps:next 方法的參數(shù)表示上一個yield表達式的返回值,所以在第一次使用next方法時,傳遞參數(shù)是無效的

9.for...of循環(huán)可以自動遍歷 Generator 函數(shù)時生成的Iterator對象,且此時不再需要調(diào)用next方法;

function *foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for ( let v of foo() ) { console.log(v); } // 1 2 3 4 5

上述代碼,使用了 for ... of 循環(huán) , 依次打印了5個 yield 表達式的值,此時就不需要 next 方法了,但是要注意的是,一旦 next 方法的返回對象的 done 屬性為 true時,for...of循環(huán)就會中止,且不包含該返回對象,因此上面 return 語句返回的 6,不包括在for...of循環(huán)之中。

10.Generator函數(shù)返回的遍歷器對象,還有一個return方法,可以返回給定的值,并且終結(jié)遍歷Generator函數(shù)

function *Generator() { yield 1; yield 2; yield 3; } var g = Generator(); g.next() // { value: 1, done: false } g.return('ok') // { value: "ok", done: true } g.next() // { value: undefined, done: true }

遍歷器對象 g 調(diào)用 return 方法后,返回值的 value 屬性就是 return 方法的參數(shù) ok(如果 不提供參數(shù),則返回值的 value 屬性為 undefined) 。并且,Generator函數(shù)的遍歷就終止了,返回值的 done 屬性為 true,以后再調(diào)用 next 方法,value 總是返回 undefined , done 屬性總是返回 true;

11.如果在 Generator 函數(shù)內(nèi)部,調(diào)用另一個 Generator 函數(shù),默認情況下是沒有效果的。

function *Generator1() { yield 'a'; yield 'b'; } function *Generator2() { yield 'x'; Generator1(); yield 'y'; } for (let v of Generator2()){ console.log(v); } // "x" // "y"

12.可以使用 yield* 表達式,用來在一個 Generator 函數(shù)里面執(zhí)行另一個 Generator 函數(shù)來達到在 Generator 函數(shù)內(nèi)部,調(diào)用另一個 Generator 函數(shù)的效果;

function *Generator1() { yield 'a'; yield 'b'; } function *Generator2() { yield 'x'; yield* Generator1(); yield 'y'; } // 等同于 function *Generator2() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同于 function *Generator2() { yield 'x'; for (let v of Generator2()) { yield v; } yield 'y'; } for (let v of Generator2()){ console.log(v); } // "x" // "a" // "b" // "y"

13.Generator 可以暫停函數(shù)執(zhí)行,返回任意表達式的值。這種特點使得 Generator 有多種應(yīng)用場景;

  • 異步操作的同步化表達 , 可以利用 Generator 函數(shù)的暫停執(zhí)行的效果,把異步操作寫在 yield 表達式里面,等到調(diào)用next方法時再往后執(zhí)行
    function *loading() {
    showLoadingScreen();
    yield loadDataAsync();
    hideLoadingScreen();
    }
    var loader = loading();
    // 加載loading
    loader.next()

    // 隱藏loadingloader.next()

上面代碼中,第一次調(diào)用 loading 函數(shù)時,該函數(shù)不會執(zhí)行,僅返回一個遍歷器。下一次對該遍歷器調(diào)用next方法,則會顯示Loading界面(showLoadingScreen),并且異步加載數(shù)據(jù)(loadDataAsync)。等到數(shù)據(jù)加載完成,再一次使用next方法,則會隱藏Loading界面,整個邏輯就很清晰了~

  • 可以利用 Generator 函數(shù)用同步的方式部署 Ajax 操作

    function *main(url) { var result = yield request( url ); var resp = JSON.parse(result); console.log(resp.value); } function request(url) { makeAjaxCall(url, function(response){ it.next(response); }); } var ajax = main(); ajax.next();

上面代碼的 main 函數(shù),就是通過 Ajax 操作獲取數(shù)據(jù),要注意的是 makeAjaxCall 函數(shù)中的 next 方法,必須加上 response 參數(shù),因為 yield 表達式,本身是沒有值的,總是等于undefined;

  • 控制流管理,如果有一個多步操作非常耗時,采用回調(diào)函數(shù),可能會寫成下面這樣:

    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 })

采用 Generator 函數(shù)來寫 :

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 } }

然后使用一個自動化函數(shù),按次序執(zhí)行所有步驟

scheduler(longRunningTask(initialValue));function scheduler(task) { var taskObj = task.next(task.value); // 如果Generator函數(shù)未結(jié)束,就繼續(xù)調(diào)用 if (!taskObj.done) { task.value = taskObj.value scheduler(task); } }

14.另外,Generator 函數(shù)是協(xié)程在 ES6 的實現(xiàn),最大特點就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行),遇到 yield 命令就暫停,等到執(zhí)行權(quán)返回,再從暫停的地方繼續(xù)往后執(zhí)行,另外,它的函數(shù)體內(nèi)外的數(shù)據(jù)交換和錯誤處理機制的特點使它可以作為異步編程的完整解決方案;




轉(zhuǎn)載于:https://www.cnblogs.com/wntd/p/9013316.html

總結(jié)

以上是生活随笔為你收集整理的ES6必知必会 (七)—— Generator 函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。