摘要:返回的遍歷器對(duì)象可以依次遍歷函數(shù)內(nèi)部的每一個(gè)狀態(tài)。示例內(nèi)部捕獲外部捕獲內(nèi)部捕獲外部捕獲上面代碼遍歷器對(duì)象連續(xù)拋出兩個(gè)錯(cuò)誤,第一個(gè)被函數(shù)體內(nèi)的捕獲。上面代碼中,首先執(zhí)行函數(shù),獲取遍歷器對(duì)象,然后使用方法第二行,執(zhí)行異步任務(wù)的第一階段。
認(rèn)識(shí)generator函數(shù)參考 來(lái)源《ecmascript6 入門(mén)》generator部分
形式上,generator函數(shù)有兩個(gè)特點(diǎn):一是function關(guān)鍵字與函數(shù)名之間有一個(gè)*。二是函數(shù)體內(nèi)使用yield語(yǔ)句,如下代碼。(yield在英語(yǔ)中意思就是 產(chǎn)出)
function* helloWorld(){ yield ‘hello’; yield ‘world’; return ‘ending’; } var hw=helloWorld();
調(diào)用執(zhí)行,調(diào)用generator函數(shù)和調(diào)用普通函數(shù)的形式一樣,沒(méi)有區(qū)別,比如上面helloWorld()。
但是內(nèi)部的執(zhí)行與普通函數(shù)是完全不同,調(diào)用generator函數(shù)之后,該函數(shù)并不執(zhí)行,返回的也不是函數(shù)運(yùn)行結(jié)果,而是一個(gè)指向內(nèi)部狀態(tài)的指針對(duì)象,也就是遍歷器對(duì)象。也就是說(shuō)generator函數(shù)還是一個(gè)遍歷器對(duì)象生成函數(shù)。返回的遍歷器對(duì)象可以依次遍歷generator函數(shù)內(nèi)部的每一個(gè)狀態(tài)。
它是怎么遍歷的呢?。遍歷器對(duì)象每次調(diào)用next方法,內(nèi)部指針就從函數(shù)頭部或者上一次停下來(lái)的的地方開(kāi)始執(zhí)行,遇到y(tǒng)ield語(yǔ)句暫停并返回一個(gè)對(duì)象,下一次調(diào)用next,遇到下一個(gè)yield暫停并返回一個(gè)對(duì)象(對(duì)象擁有value,和done屬性)。value的值就是yield語(yǔ)句的值,done屬性表示遍歷是否結(jié)束(false沒(méi)有結(jié)束,true結(jié)束)。
上面示例代碼用調(diào)用4次next:
第一次調(diào)用next,generator函數(shù)開(kāi)始執(zhí)行,遇到第一個(gè)yield暫停,并且返回一個(gè)對(duì)象,value =hello,done=false表示還遍歷還沒(méi)有結(jié)束。
第二次調(diào)用next,從上次暫停的位置開(kāi)始執(zhí)行,遇到下一個(gè)yield暫停,并返回一個(gè)對(duì)象。。
第三次調(diào)用next,返回value為return的值,done為true表示遍歷結(jié)束。
第四次調(diào)用next,generator函數(shù)已經(jīng)運(yùn)行完畢,返回value為undefined,done為true。
yield和return;yield語(yǔ)句與return語(yǔ)句既有相似之處 ,也有區(qū)別。相似之處在于都可以返回緊跟在其后邊的表達(dá)式的值。區(qū)別在于每次遇到y(tǒng)ield,函數(shù)暫停,下一次在從該位置向后執(zhí)行,而return語(yǔ)句沒(méi)有此位置記憶功能,一個(gè)函數(shù)里面只能執(zhí)行一次,而yield正因?yàn)榭梢杂卸鄠€(gè),可以返回多個(gè)值,所以generator函數(shù)可以返回一系列的值,這也就是它名稱(chēng)的來(lái)歷(generator英語(yǔ)意思為生成器)
與Iterator接口的關(guān)系任意一個(gè)對(duì)象的iterator接口都是部署在了Symbol.iterator屬性,由于generator函數(shù)就是遍歷器生成函數(shù),所以可以直接把它賦值給Symbol.iterator,從而使的該對(duì)象具有Iterator接口。
示例:
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3]
說(shuō)明:代碼中generator函數(shù)賦給了myIterable對(duì)象的Symbol.iterator屬性,使的該對(duì)象具有iterator接口,可以 被(…)運(yùn)算符遍歷。為什么是這樣?(…)三個(gè)點(diǎn)這里叫做擴(kuò)展運(yùn)算符,它的執(zhí)行是調(diào)用了遍歷器方法(它可以將一個(gè)數(shù)組轉(zhuǎn)為用逗號(hào)分割的序列,可以用于函數(shù)調(diào)用傳參),這里就是generator函數(shù),然后返回一個(gè)遍歷器對(duì)象,然后重復(fù)調(diào)用它的next方法。其實(shí)不只有擴(kuò)展運(yùn)算符,for..of循環(huán)的執(zhí)行也是調(diào)用的iterator接口方法,也就是說(shuō)只有部署了iterator接口的數(shù)據(jù)集合才可以使用for...of,擴(kuò)展運(yùn)算符遍歷。
Generator.prototype.throw()Generator函數(shù)返回的遍歷器對(duì)象,都有一個(gè)throw方法,可以在函數(shù)體外拋出錯(cuò)誤,然后在Generator函數(shù)體內(nèi)捕獲。
示例:
var g = function* () { try { yield; } catch (e) { console.log("內(nèi)部捕獲", e); } }; var i = g(); i.next(); try { i.throw("a"); i.throw("b"); } catch (e) { console.log("外部捕獲", e); } // 內(nèi)部捕獲 a // 外部捕獲 b
上面代碼遍歷器對(duì)象連續(xù)拋出兩個(gè)錯(cuò)誤,第一個(gè)被generator函數(shù)體內(nèi)的catch捕獲。第二個(gè)由于generator函數(shù)體內(nèi)的catch已經(jīng)執(zhí)行過(guò)了,所以被外面的catch捕獲。如果generator函數(shù)體內(nèi)沒(méi)有try...catch...語(yǔ)句,那么就會(huì)被外面的catch語(yǔ)句捕獲。如果都沒(méi)有try...catch...,那么程序報(bào)錯(cuò)。
5.Generator.prototype.return()
yield*語(yǔ)句如果在 Generator 函數(shù)內(nèi)部,調(diào)用另一個(gè) Generator 函數(shù),默認(rèn)情況下是沒(méi)有效果的。yield*語(yǔ)句可以用來(lái)在一個(gè) Generator 函數(shù)里面執(zhí)行另一個(gè) Generator 函數(shù)。
function* foo() { yield "a"; yield "b"; } function* bar() { yield "x"; yield* foo(); yield "y"; } // 等同于 function* bar() { yield "x"; yield "a"; yield "b"; yield "y"; } // 等同于 function* bar() { yield "x"; for (let v of foo()) { yield v; } yield "y"; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y"
從語(yǔ)法角度看,如果yield命令后面跟的是一個(gè)遍歷器對(duì)象,需要在yield命令后面加上星號(hào),表明它返回的是一個(gè)遍歷器對(duì)象。這被稱(chēng)為yield*語(yǔ)句。
let delegatedIterator = (function* () { yield "Hello!"; yield "Bye!"; }()); let delegatingIterator = (function* () { yield "Greetings!"; yield* delegatedIterator; yield "Ok, bye."; }()); for(let value of delegatingIterator) { console.log(value); } // "Greetings! // "Hello!" // "Bye!" // "Ok, bye.”
yield*后面的Generator函數(shù)(沒(méi)有return語(yǔ)句時(shí)),等同于在Generator函數(shù)內(nèi)部,部署一個(gè)for...of循環(huán)。
function* concat(iter1, iter2) { yield* iter1; yield* iter2; } // 等同于 function* concat(iter1, iter2) { for (var value of iter1) { yield value; } for (var value of iter2) { yield value; } }
上面代碼,yield* 執(zhí)行的是一個(gè)遍歷器,for...of...循環(huán)的也是一個(gè)遍歷器,所以for...of...返回yield value時(shí)等同于yield*。
兩個(gè)平常會(huì)用到的示例:
1)遍歷嵌套的數(shù)組:
function* iterTree(tree) { if (Array.isArray(tree)) { for(let i=0; i < tree.length; i++) { yield* iterTree(tree[i]); } } else { yield tree; } } const tree = [ "a", ["b", "c"], ["d", "e"] ]; for(let x of iterTree(tree)) { console.log(x); } // a // b // c // d // e
2)對(duì)于狀態(tài)的控制:
var clock = function*() { while (true) { console.log("Tick!"); yield; console.log("Tock!"); yield; } };作為對(duì)象屬性的generator
如果一個(gè)對(duì)象的屬性是**Generator**函數(shù),可以簡(jiǎn)寫(xiě)成下面的形式
let obj = { * myGeneratorMethod() { ··· } }; 等同于 let obj = { myGeneratorMethod: function* () { // ··· } };Generator函數(shù)的this
Generator函數(shù)總是返回一個(gè)遍歷器,ES6規(guī)定這個(gè)遍歷器是Generator函數(shù)的實(shí)例,也繼承了Generator函數(shù)的prototype對(duì)象上的方法。
function* g() {} g.prototype.hello = function () { return "hi!"; }; let obj = g(); obj instanceof g // true obj.hello() // "hi!"
上面代碼表明,Generator函數(shù)g返回的遍歷器obj,是g的實(shí)例,而且繼承了g.prototype。但是,如果把g當(dāng)作普通的構(gòu)造函數(shù),并不會(huì)生效,因?yàn)間返回的總是遍歷器對(duì)象,而不是this對(duì)象。所以如果在generator函數(shù)內(nèi)使用this,obj對(duì)象訪問(wèn)不到。
那么,有沒(méi)有辦法讓Generator函數(shù)返回一個(gè)正常的對(duì)象實(shí)例,既可以用next方法,又可以獲得正常的this?
function* F() { this.a = 1; yield this.b = 2; yield this.c = 3; } var f = F.call(F.prototype); f.next(); // Object {value: 2, done: false} f.next(); // Object {value: 3, done: false} f.next(); // Object {value: undefined, done: true} f.a // 1 f.b // 2 f.c // 3
上面代碼:首先使用call函數(shù)將F函數(shù)的this綁定到F.prototype;而f還是那個(gè)遍歷器對(duì)象是F函數(shù)的實(shí)例,又可以繼承F.prototype的屬性,所以也就可以訪問(wèn)F.prototype代表的this的屬性了。
Generator函數(shù)的應(yīng)用generator函數(shù)最大的作用可以用作異步任務(wù)的封裝(由于它的yield命令特性,可以暫停和恢復(fù)執(zhí)行)。而之前javascript對(duì)于異步的實(shí)現(xiàn)主要就是 回調(diào)函數(shù),事件監(jiān)聽(tīng),promise等。
示例:
var fetch = require("node-fetch"); function* gen(){ var url = "https://api.github.com/users/github"; var result = yield fetch(url); console.log(result.bio); }
上面代碼中,Generator 函數(shù)封裝了一個(gè)異步操作,該操作先讀取一個(gè)遠(yuǎn)程接口,然后從 JSON 格式的數(shù)據(jù)解析信息。就像前面說(shuō)過(guò)的,這段代碼非常像同步操作,除了加上了yield命令。
var g = gen(); var result = g.next(); result.value.then(function(data){ return data.json(); }).then(function(data){ g.next(data); });
上面代碼中,首先執(zhí)行 Generator 函數(shù),獲取遍歷器對(duì)象,然后使用next方法(第二行),執(zhí)行異步任務(wù)的第一階段。由于Fetch模塊返回的是一個(gè) Promise 對(duì)象,而這個(gè)對(duì)象被yield返回到了它的value屬性中,因此要用.value.then方法調(diào)用then方法。成功后 return數(shù)據(jù)參數(shù)data可以被第二個(gè)then方法中接受。而第二次調(diào)用then方法傳入的data又傳回了gen函數(shù)給了變量result。value往出傳值,next可以往里傳值。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/82147.html
摘要:調(diào)用函數(shù)后和普通函數(shù)不同的是,該函數(shù)并不立即執(zhí)行,也不返回函數(shù)執(zhí)行結(jié)果,而是返回一個(gè)指向內(nèi)部狀態(tài)的對(duì)象,也可以看作是一個(gè)遍歷器對(duì)象。第一個(gè)只是用來(lái)啟動(dòng)函數(shù)內(nèi)部的遍歷器,傳參也沒(méi)有多大意義。 之前斷斷續(xù)續(xù)接觸到了一些ES6的知識(shí),異步編程方面聽(tīng)得比較多的就是Promise,直到最近比較系統(tǒng)地學(xué)習(xí)了ES6的新特性才發(fā)現(xiàn)Generator這個(gè)神奇的存在,它可以實(shí)現(xiàn)一些前所未有的事情,讓我頓時(shí)...
摘要:同時(shí),迭代器有一個(gè)方法來(lái)向函數(shù)中暫停處拋出一個(gè)錯(cuò)誤,該錯(cuò)誤依然可以通過(guò)函數(shù)內(nèi)部的模塊進(jìn)行捕獲處理。 本文翻譯自:Diving Deeper With ES6 Generators 由于個(gè)人能力有限,翻譯中難免有紕漏和錯(cuò)誤,望不吝指正issue ES6 Generators:完整系列 The Basics Of ES6 Generators Diving Deeper With E...
摘要:關(guān)于協(xié)程和中的什么是協(xié)程進(jìn)程和線程眾所周知,進(jìn)程和線程都是一個(gè)時(shí)間段的描述,是工作時(shí)間段的描述,不過(guò)是顆粒大小不同,進(jìn)程是資源分配的最小單位,線程是調(diào)度的最小單位。子程序就是協(xié)程的一種特例。 關(guān)于協(xié)程和 ES6 中的 Generator 什么是協(xié)程? 進(jìn)程和線程 眾所周知,進(jìn)程和線程都是一個(gè)時(shí)間段的描述,是CPU工作時(shí)間段的描述,不過(guò)是顆粒大小不同,進(jìn)程是 CPU 資源分配的最小單位,...
摘要:示例運(yùn)行函數(shù)彈出彈出函數(shù)接收參數(shù),返回值。其中,返回一個(gè)對(duì)象,是的返回值,代表函數(shù)是否執(zhí)行完成。 ES6特性介紹(下) ES6新的標(biāo)準(zhǔn),新的語(yǔ)法特征:1、變量/賦值2、函數(shù)3、數(shù)組/json4、字符串5、面向?qū)ο?、Promise7、generator8、ES7:async/await 《【W(wǎng)eb全棧課程二】ES6特性介紹(上)》見(jiàn):https://segmentfault.com/a...
摘要:在函數(shù)定義上使用關(guān)鍵字來(lái)表示方法調(diào)用時(shí)返回的值。是一個(gè)有屬性的。這個(gè)指向一個(gè)函數(shù),這個(gè)函數(shù)返回關(guān)于這個(gè)對(duì)象的。在中所有的集合類(lèi)對(duì)象和字符串都是,并且有自己默認(rèn)的。注意本身是不返回任何值的,它只向外部產(chǎn)生值。 ES6新特性 iterators and Generators ES6中引入了許多新特性,目前大量的JavaScript項(xiàng)目已經(jīng)使用了ES6來(lái)進(jìn)行開(kāi)發(fā),那么熟悉這些新的特性是十分必...
摘要:由于可以使用語(yǔ)句來(lái)暫停異步操作,這讓異步編程的代碼,很像同步數(shù)據(jù)流方法一樣。該臨時(shí)函數(shù)就叫做函數(shù)。下面就是簡(jiǎn)單的函數(shù)轉(zhuǎn)換器。 訪問(wèn)原文地址 對(duì)ES6的generators的介紹分為3個(gè)部分 第一部分base介紹及使用 第二部分基于generators和Promise實(shí)現(xiàn)最強(qiáng)大的異步處理邏輯 概述 Generator函數(shù)是協(xié)程在ES6的實(shí)現(xiàn),用來(lái)做異步流程的封裝,最大特點(diǎn)就是可以交出...
閱讀 1045·2021-09-22 15:26
閱讀 2624·2021-09-09 11:52
閱讀 1917·2021-09-02 09:52
閱讀 2254·2021-08-12 13:28
閱讀 1192·2019-08-30 15:53
閱讀 522·2019-08-29 13:47
閱讀 3393·2019-08-29 11:00
閱讀 3105·2019-08-29 10:58