16Generator函數(shù)的方法

簡(jiǎn)介

基本概念

Generator 是 ES6 提供的一種異步編程解決方案⊙糟澹可以把它理解成一個(gè)狀態(tài)機(jī)隆箩,并且會(huì)返回一個(gè)遍歷器對(duì)象

  1. 有以下兩個(gè)特征
  • function關(guān)鍵字和函數(shù)名之間有一個(gè)星號(hào)*
  • 函數(shù)體內(nèi)部使用yield表達(dá)式
  1. 執(zhí)行大致過(guò)程
  • 生成 Iterator 之后响驴,第一次調(diào)用next方法,直到遇見(jiàn)第一個(gè)yield表達(dá)式為止拖云。此時(shí)返回一個(gè)對(duì)象贷笛,value就是yield表達(dá)式的值,同時(shí)done為false
  • 按照此規(guī)律執(zhí)行应又,當(dāng)執(zhí)行到return語(yǔ)句是宙项,next方法還是返回一個(gè)對(duì)象,該對(duì)象的value就是return返回的值株扛,同時(shí)done為ture
  • 如果沒(méi)有return尤筐,執(zhí)行完所有的yielddone還是為false洞就。需要再來(lái)一下next

yield 表達(dá)式

  1. generator 函數(shù)中可以不使用yield盆繁,此時(shí)變成了一個(gè)單純的暫緩執(zhí)行函數(shù)
  2. 在普通函數(shù),匿名函數(shù)中使用yield表達(dá)式旬蟋,會(huì)產(chǎn)生一個(gè)錯(cuò)誤
  3. 如果yield表達(dá)式在另一個(gè)表達(dá)式中油昂,必須放在圓括號(hào)內(nèi)
// 情景3示例
function* demo() {
  console.log('Hello' + (yield 99)); // OK
  console.log('Hello' + (yield 123)); // OK
}

next 方法參數(shù)

next 方法可以帶一個(gè)參數(shù),該參數(shù)會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值

基礎(chǔ)案例分析

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}
var b = foo(5);
b.next();  // {value:6,done:false}
b.next(12);  // {value:8,done:false}
// 此時(shí) x=5,y=24,z=13
b.next(13);  // {value:42,done:true}
  1. 第一個(gè)使用next不需要傳值

進(jìn)階案例分析

function* dataConsumer() {
  console.log('Started');
  console.log(`1. ${yield}`);
  console.log(`2. ${yield}`);
  return 'result';
}
let genObj = dataConsumer();
genObj.next();
genObj.next('a');
genObj.next('b');
  1. 第一次調(diào)用next,執(zhí)行到第二條語(yǔ)句暫停倾贰。因此打印'Started'
  2. 第二次調(diào)用next,傳入a冕碟,作為上次yield的返回值,打印1. a匆浙。繼續(xù)執(zhí)行到yield暫停
  3. 第三次調(diào)用next,傳入b安寺,作為上次yield的返回值,答應(yīng)2. b首尼。繼續(xù)執(zhí)行挑庶,此時(shí){value:'result',done:true}

for...of循環(huán)

斐波那契數(shù)列

function* feibonacci() {
  let [prev,curr] = [0,1];
  for (;;) {
    [prev,curr] = [curr,prev+curr]
    yield curr;
  }
}
// num 是值的大小,不是個(gè)數(shù)
for (let num of feibonacci()) {
  if (num > 100) break; // ---> break 會(huì)觸發(fā)遍歷器的return方法软能,return 方法其實(shí)一般就是返回`return {done:true}`
  console.log(num);
}

對(duì)象擴(kuò)展遍歷器

在對(duì)象的[Symbol.iterator]上掛載遍歷器

function* objectEntries() {
  let objKeys = Object.keys(this);
  for (let key of objKeys) {
    yield [key,this[key]]
  }
}
let jane = { first: 'Jane', last: 'Doe'};
// 只能綁定到單個(gè)的對(duì)象上
jane[Symbol.iterator] = objectEntries;
for (let [key,val] of jane) {
  console.log(key,val)
}

throw方法

遍歷器對(duì)象都有一個(gè)throw方法迎捺,在外部拋出錯(cuò)誤之后,內(nèi)部能夠捕獲

基礎(chǔ)知識(shí)

外部拋出錯(cuò)誤

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
  1. 遍歷器i執(zhí)行next方法
  2. 遍歷器i在外部拋出錯(cuò)誤查排,被內(nèi)部的捕獲凳枝。同時(shí),throw方法傳遞的參數(shù)當(dāng)作catch的參數(shù)
  3. 遍歷器再次拋出錯(cuò)誤,catch已經(jīng)執(zhí)行過(guò)了雹嗦,就不執(zhí)行了范舀,因此被外部的try catch捕獲
  4. 可以內(nèi)部捕獲外部拋出的錯(cuò)誤,也可以外部捕獲拋出的錯(cuò)誤
  5. throw方法了罪,也就是拋出錯(cuò)誤的方法锭环,默認(rèn)執(zhí)行一次next

內(nèi)部拋出錯(cuò)誤

function* foo() {
  var x = yield 3;
  var y = x.toUpperCase();
  yield y;
}

var it = foo();

it.next(); // { value:3, done:false }

try {
  it.next(42);
} catch (err) {
  console.log(err);
}
  1. 由于next(42)后,x為數(shù)字42沒(méi)有toUpperCase方法泊藕。因此辅辩,將拋出一個(gè)錯(cuò)誤
  2. 拋出的錯(cuò)誤將在外部被捕獲

return方法

  1. 使用return方法
  • 當(dāng)有參數(shù)時(shí),返回給定的值
  • 當(dāng)沒(méi)有參數(shù),返回undefined
  1. Generator函數(shù)內(nèi)有try...finally代碼塊玫锋,那么return方法會(huì)推遲到finally代碼執(zhí)行完再執(zhí)行
function* gen() {
  yield 1;
  yield 2;
}
var g = gen();
g.next(); // {value: 1,done: false}
g.return('foo'); // {value: 'foo',done: true}
g.next(); // {value: undefined,done: false}
// finally
function* numbers () {
  try {
    yield 2;
  } finally {
    yield 4;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false } --- 執(zhí)行finally中語(yǔ)句
g.next() // { value: 7, done: true } --- 執(zhí)行完finnally中語(yǔ)句之后蛾茉,再執(zhí)行return語(yǔ)句

next,throw,return 共同點(diǎn)

它們作用都是讓 Generator 函數(shù)回復(fù)執(zhí)行

yield*表達(dá)式

yield*表達(dá)式,代表它后面是一個(gè)遍歷器對(duì)象撩鹿,在沒(méi)有return語(yǔ)句的情況下谦炬,相當(dāng)于使用for of

  1. Generator函數(shù)中不能直接調(diào)用Generator函數(shù),需要使用yield*

基本案例

function* foo() {
  yield 'a';
  yield 'b';
}
function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}
// Generator bar 相當(dāng)于
function* bar() {
  yield 'x';
  for (let v of foo()) {
    yield v;
  } 
  yield 'y';
}
// 調(diào)用 bar
for (let v of bar()) {
  console.log(v);
}
// 結(jié)果是:x,a,b,y

yield*后邊值的種類

任何數(shù)據(jù)結(jié)構(gòu)只要有 Iterator 接口节沦,就可以被yield*遍歷键思。

  1. yield* 'hello'
  2. yield* [1,2,3]

有return的Generator函數(shù)

yield* foo(),如果foo函數(shù)有return甫贯,那么該表達(dá)式是有返回值的

function *foo() {
  yield 2;
  yield 3;
  return "foo";
}

function *bar() {
  yield 1;
  var v = yield *foo();
  console.log( "v: " + v );
  yield 4;
}

var it = bar();

it.next()
// {value: 1, done: false}
it.next()
// {value: 2, done: false}
it.next()
// {value: 3, done: false}
it.next();
// 相當(dāng)于沒(méi)有調(diào)用 next 方法吼鳞,只是將返回值返回給表達(dá)式
// "v: foo"
// {value: 4, done: false}
it.next()
// {value: undefined, done: true}

取出嵌套數(shù)組

function* tree(arr) {
    if (Array.isArray(arr)) {
        for (let i = 0;i < arr.length;i++) {
            yield* tree(arr[i]);
        }
    }else {
        yield arr;
    }
}
const arr = [1,[2,3],[4,5],6,7]
for (let i of tree(arr)) {
    console.log(i);
}
  1. 注意兩個(gè)地方
  • 如果使用yield tree,也就是沒(méi)有*,那么調(diào)用Generator方法是沒(méi)有什么效果的
  • 使用let聲明的變量進(jìn)行遞歸沒(méi)有關(guān)系

作為對(duì)象屬性的Generator函數(shù)

簡(jiǎn)寫成下面的形式

let obj = {
  * myGeneratorMethod() {

  }
}

Generator 函數(shù)的this

Generator的返回值

  1. Generator 函數(shù)總是返回一個(gè)遍歷器叫搁,這個(gè)遍歷器是 Generator 的實(shí)例
  2. 使用instanceof檢測(cè)赔桌,為true
  3. Generator 原型上的方法,將被遍歷器繼承
function* p() {

}
p.property.say = function () {
  console.log('hello')
}
let s = p();
s instanceof p;  // true
s.say();  // 'hello'

Generator 中 this 基礎(chǔ)知識(shí)

  1. 即使“返回值”是“Generator”函數(shù)實(shí)例渴逻,但是“Generator”中this上綁定的屬性疾党,“返回值”上并不會(huì)有
  2. 不能對(duì) Generator 函數(shù)直接使用 new 命令

應(yīng)用

異步操縱的同步化表達(dá)

// loading 應(yīng)用
function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
loader.next();
loader.next();
  1. 第一次執(zhí)行next方法時(shí),展示加載頁(yè)面裸卫,同時(shí)發(fā)送異步請(qǐng)求
  2. 其實(shí)第二次執(zhí)行next方法仿贬,應(yīng)該是異步請(qǐng)求完畢的時(shí)候
// Ajax 應(yīng)用
function request(url){
  // 是個(gè)偽代碼,其實(shí)就是一個(gè)發(fā)送 ajax 請(qǐng)求的方法
  makeAjaxCall(url,function (response) {
    it.next(reponse);
  })
}
function* main() {
  var result = yield request('http://some.url');
  var resp = JSON.parse(result);
  console.log(resp.value)
}
var it = main();
it.next();
  1. 第一次執(zhí)行next方法墓贿,發(fā)送ajax請(qǐng)求
  2. 在成功響應(yīng)的回調(diào)函數(shù)中茧泪,第二次執(zhí)行next方法,該方法將response當(dāng)作上一次的返回值

控制流管理

這里只介紹最簡(jiǎn)單的一種方法

let steps = [step1Func, step2Func, step3Func];

function *iterateSteps(steps){
  for (var i=0; i< steps.length; i++){
    var step = steps[i];
    yield step();
  }
}
// 之后使用 for of 即可
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末聋袋,一起剝皮案震驚了整個(gè)濱河市队伟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌幽勒,老刑警劉巖嗜侮,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異啥容,居然都是意外死亡锈颗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門咪惠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)击吱,“玉大人,你說(shuō)我怎么就攤上這事遥昧「泊迹” “怎么了朵纷?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)永脓。 經(jīng)常有香客問(wèn)我袍辞,道長(zhǎng),這世上最難降的妖魔是什么常摧? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任搅吁,我火速辦了婚禮,結(jié)果婚禮上排宰,老公的妹妹穿的比我還像新娘似芝。我一直安慰自己那婉,他們只是感情好板甘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著详炬,像睡著了一般盐类。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呛谜,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天在跳,我揣著相機(jī)與錄音,去河邊找鬼隐岛。 笑死猫妙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的聚凹。 我是一名探鬼主播割坠,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼妒牙!你這毒婦竟也來(lái)了彼哼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤湘今,失蹤者是張志新(化名)和其女友劉穎敢朱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摩瞎,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拴签,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旗们。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚓哩。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蚪拦,靈堂內(nèi)的尸體忽然破棺而出杖剪,到底是詐尸還是另有隱情冻押,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布盛嘿,位于F島的核電站洛巢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏次兆。R本人自食惡果不足惜稿茉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望芥炭。 院中可真熱鬧漓库,春花似錦、人聲如沸园蝠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)彪薛。三九已至茂装,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間善延,已是汗流浹背少态。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留易遣,地道東北人彼妻。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像豆茫,于是被迫代替她去往敵國(guó)和親侨歉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 在此處先列下本篇文章的主要內(nèi)容 簡(jiǎn)介 next方法的參數(shù) for...of循環(huán) Generator.prototy...
    醉生夢(mèng)死閱讀 1,452評(píng)論 3 8
  • 簡(jiǎn)介 基本概念 Generator函數(shù)是ES6提供的一種異步編程解決方案澜薄,語(yǔ)法行為與傳統(tǒng)函數(shù)完全不同为肮。本章詳細(xì)介紹...
    呼呼哥閱讀 1,076評(píng)論 0 4
  • 異步編程對(duì)JavaScript語(yǔ)言太重要。Javascript語(yǔ)言的執(zhí)行環(huán)境是“單線程”的肤京,如果沒(méi)有異步編程颊艳,根本...
    呼呼哥閱讀 7,313評(píng)論 5 22
  • 本文作者就是我,簡(jiǎn)書的microkof忘分。如果您覺(jué)得本文對(duì)您的工作有意義棋枕,產(chǎn)生了不可估量的價(jià)值,那么請(qǐng)您不吝打賞我妒峦,...
    microkof閱讀 23,735評(píng)論 16 78
  • 《愛(ài)的五種語(yǔ)言》第46天: 服務(wù)行動(dòng)重斑,精心時(shí)刻 平鍋帶兒子去理發(fā),外加拍證件照肯骇,拍完發(fā)照片給我窥浪,兒子今天的發(fā)型好帥...
    路西法媽媽閱讀 173評(píng)論 0 0