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