成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6 Features系列:GeneratorFunction介紹

golden_hamster / 591人閱讀

摘要:沒有顯示顯示顯示關(guān)鍵字迭代器生成器用于馬上退出代碼塊并保留現(xiàn)場,當(dāng)執(zhí)行迭代器的函數(shù)時(shí),則能從退出點(diǎn)恢復(fù)現(xiàn)場并繼續(xù)執(zhí)行下去。迭代器迭代器是一個(gè)擁有方法和方法的對象,通過函數(shù)不斷執(zhí)行以關(guān)鍵字分割的代碼段,通過函數(shù)令分割的代碼段拋出異常。

一、前言                           

第一次看koajs的示例時(shí),發(fā)現(xiàn)該語句 function *(next){...............} ,這是啥???于是搜索一下,原來這是就是ES6的新特性Generator Function(生成器函數(shù))。

那什么是生成器函數(shù)呢?其實(shí)就相當(dāng)于C#2.0中通過yield關(guān)鍵字實(shí)現(xiàn)的迭代器的生成器(細(xì)節(jié)有所不同),那么理解的關(guān)鍵就在迭代器和yield關(guān)鍵字兩部分了。下面將嘗試從表象出發(fā),逐步對生成器函數(shù)及利用它進(jìn)行異步編程進(jìn)行淺層的分析理解。

二、表象——語法及基本使用                   

示例:

// 定義生成器函數(shù)
function *enumerable(msg){
  console.log(msg)
  var msg1 = yield msg + "  after "
  console.log(msg1)
  var msg2 = yield msg1 + " after"
  try{
    var msg3 = yield msg2 + "after"
    console.log("ok")
  }
  catch(e){
    console.log(e)
  }
  console.log(msg2 + " over")
}

// 初始化迭代器
var enumerator = enumerable("hello")
var ret = enumerator.next() // 控制臺顯示 hello,ret的值{value:"hello after",done:false}
ret =  enumerator.next("world") // 控制臺顯示 world,ret的值{value:"world after",done:false}
ret = enumerator.next("game") // 控制臺顯示game,ret的值{value:"game after",done:false}
// 拋出異常信息
ret = enumerator.throw(new Error("test")) // 控制臺顯示new Error("test")信息,然后顯示game over。ret的值為{done:true}

// for...of語句
enumerator = enumerable("hello")
for(ret of enumerator)
  console.log(JSON.stringify(ret));
// 控制臺依次顯示
// hello
// {value:"hello after",done:false}
// world
// {value:"world after",done:false}
// {value:"game after",done:false}
// game over
// {done:true}
1. 生成器語函數(shù)定義
function* test(){}
function * test(){}
function *test(){}
test = function* (){} 
test = function *(){}

普通函數(shù)添加*號后則成為了成為了生成器函數(shù)了。

Object.prototype.toString.call(test) // 顯示[object GeneratorFunction]
  生成器函數(shù)的行為與普通函數(shù)并不相同,表現(xiàn)為如下3點(diǎn):
  1. 通過new運(yùn)算符或函數(shù)調(diào)用的形式調(diào)用生成器函數(shù),均會返回一個(gè)生成器實(shí)例;
  2. 通過new運(yùn)算符或函數(shù)調(diào)用的形式調(diào)用生成器函數(shù),均不會馬上執(zhí)行函數(shù)體的代碼;
  3. 必須調(diào)用生成器實(shí)例的next方法才會執(zhí)行生成器函數(shù)體的代碼。

function *say(msg){
  console.log(msg)
}
var gen = say("hello world") // 沒有顯示hello world
console.log(Object.prototype.toString.call(gen)) // 顯示[object Generator]
gen.next() // 顯示hello world
2、 關(guān)鍵字yield——迭代器生成器

用于馬上退出代碼塊并保留現(xiàn)場,當(dāng)執(zhí)行迭代器的next函數(shù)時(shí),則能從退出點(diǎn)恢復(fù)現(xiàn)場并繼續(xù)執(zhí)行下去。下面有2點(diǎn)需要注意:
1. yield后面的表達(dá)式將作為迭代器next函數(shù)的返回值;
2. 迭代器next函數(shù)的入?yún)⒆鳛閥ield的返回值(有點(diǎn)像運(yùn)算符)。
3、迭代器(Generator)
迭代器是一個(gè)擁有 {value:{}, done:{Boolean}} next([])方法 和 {undefined} throw([*])方法 的對象,通過next函數(shù)不斷執(zhí)行以關(guān)鍵字yield分割的代碼段,通過throw函數(shù)令yield分割的代碼段拋出異常。

三、核心1——迭代器                     

迭代器更多的是指迭代器模式,迭代器模式是指通過一個(gè)名為迭代器的對象按一定的規(guī)則遍歷集合元素,調(diào)用者只需告訴迭代器獲取下一個(gè)元素即可,而集合的類型、如何獲取元素等因素均由具體的迭代器自行處理。(又一次地關(guān)注點(diǎn)分離?。┎⑶矣捎诘髂J娇梢宰龅?按需執(zhí)行/延遲執(zhí)行 的效果,因此能降低遍歷無限序列時(shí)內(nèi)存/棧溢出的問題,也能作為異步編程模式使用。
模式理解的注意點(diǎn):
1. 迭代器每次進(jìn)訪問集合的一個(gè)元素,并由調(diào)用者發(fā)起訪問請求時(shí)迭代器才執(zhí)行下一次訪問操作
2. “按一定的規(guī)則”,意味著不一定遍歷集合中所有的元素,并且規(guī)則可以內(nèi)聚到迭代器的具體實(shí)現(xiàn)上,也可通過策略模式外移到其他模塊中;
3. “集合”,集合可以是一開始就已經(jīng)初始化好的有限序列集合(如[1,2,3,4,5,6,7]),也可以是按需生成的無限序列集合(如1到無限大)
4. “集合元素”,可以是整數(shù)集合、字符串集合等數(shù)據(jù)集合,也可以是函數(shù)等指令+數(shù)據(jù)的集合;
若觸過C#、Java等服務(wù)端語句的朋友應(yīng)該對迭代器有一定程度的了解,C#的IEnumrable、IEnumerator和Java的Iterable、Iterator就是跟迭代器相關(guān)的接口定義,繼承上述接口的迭代器實(shí)現(xiàn)均可以通過foreach或for...in語句作循環(huán)操作。

那么這里有2點(diǎn)是要注意的:
1. 迭代器是指設(shè)計(jì)模式,跟具體的語言無關(guān),因此所有語言均可根據(jù)該模式實(shí)現(xiàn)具體的迭代器;
2. foreach或for...in語句是語法層面的支持,跟迭代器模式?jīng)]有必然聯(lián)系。(若語法層面不支持,那函數(shù)式編程中的遞歸的效果是一樣的,假如編譯器/解析器支持尾遞歸則更好了,可以JS不支持)
下面我們通過迭代器來實(shí)現(xiàn)Python中的range函數(shù),并通過range函數(shù)創(chuàng)建一個(gè)超大的有限序列正整數(shù)集合(直接用數(shù)組的話絕有可能導(dǎo)致棧溢出哦!)。

// 迭代器構(gòu)造函數(shù)
var RangeIterator = function(start,end,scan){
    this.start = arguments.length >= 2 ? start : 0    
    this.end = end == undefined ? start : end
    this.scan = scan || 1
    this.idx = this.start
}
// 向迭代器發(fā)起訪問下一個(gè)元素的請求
// FF和ES6下迭代器接口規(guī)范定義了迭代器必須通過名為next的函數(shù)發(fā)起訪問下一個(gè)元素的請求
RangeIterator.prototype.next = function(){
    if (this.idx > this.end) 
    if (!!StopIteration) {
         throw StopIteration
       }else{
          return void 0
       }

    var ret = this.idx
    this.idx += this.scan
    return ret
}
// Python中的range函數(shù)
var range = function(start, end, scan){
   var iterator = new RangeIterator(start, end, scan)
   return {
       // FF下令for...in語句調(diào)用對象的迭代器的接口規(guī)范
        __iterator__: function(){
            return iterator
        },
       // 暴露迭代器的next函數(shù)
        next: function(){
            return iterator.next()
        },
        toString: function(){
            // 可能會導(dǎo)致棧溢出
            var array = []
            for (var i = this.next(); i != void 0; i = this.next())
                array.push(i)
            return array + ""
        }    
    }
}
var r = range(1, 100000000000000000000)
// FF下
// 參考:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Iterators_and_Generators#.E5.AE.9A.E4.B9.89.E8.87.AA.E5.AE.9A.E4.B9.89.E8.BF.AD.E4.BB.A3.E5.99.A8
for(var i in r)
  console.log(i) // 顯示1到99999999999999999999
// 所有瀏覽器
for (var i = r.next(); i != void 0; i = r.next())
  console.log(i) // 顯示1到99999999999999999999

由于JS是單線程運(yùn)行,并且當(dāng)UI線程被阻塞N秒后,瀏覽器會詢問是否停止腳本的執(zhí)行,但上述代碼并不會由于序列過大造成棧溢出的問題。假如預(yù)先生成1到99999999999999999999或更大數(shù)字的數(shù)組,那很有可能造成stack overflow。那是由于迭代器實(shí)質(zhì)為一狀態(tài)機(jī),而調(diào)用next函數(shù)則是觸發(fā)狀態(tài)的轉(zhuǎn)換,而狀態(tài)機(jī)中同一時(shí)刻用于存放變量的存儲空間固定,并不會出現(xiàn)無限增長的情況。

四、核心2——yield關(guān)鍵字                  

回到關(guān)鍵字yield上了,其實(shí)yield關(guān)鍵字就是以一種更直觀、便捷的方式讓我們創(chuàng)建用于遍歷有限序列集合的迭代器,而yield則用于將生成器函數(shù)的代碼切片作為有限序列集合的元素(元素的類型為指令+數(shù)據(jù),而不僅僅是數(shù)據(jù)而已)。下面我們一起看看yield關(guān)鍵字是怎樣對代碼切片的吧!

// 定義生成器函數(shù)
function *enumerable(msg){
  console.log(msg)
  var msg1 = yield msg + "  after "
  console.log(msg1)
  var msg2 = yield msg1 + " after"
  console.log(msg2 + " over")
}

上述代碼最終會被解析為下面的代碼:

var enumerable = function(msg){
  var state = -1

  return {
    next: function(val){
      switch(++state){
         case 0:
                  console.log(msg + " after")
                  break
         case 1:
                  var msg1 = val
                  console.log(msg1 + " after")
                  break
         case 2:
                  var msg2 = val
                  console.log(msg2 + " over")
                  break
      }
    }
  }
}

(注意:上述僅僅簡單的分析,更復(fù)雜的情況(條件控制、循環(huán)、迭代、異常捕獲處理等)可以參考@趙劼的《人肉反編譯使用關(guān)鍵字yield的方法》)

五、異步調(diào)用中的應(yīng)用                   

由于迭代器模式實(shí)現(xiàn) 延遲執(zhí)行/按需執(zhí)行,因此可作為一種異步編程模式來應(yīng)用。

var iterator = getArticles("dummy.json")
// 開始執(zhí)行
iterator.next()
// 異步任務(wù)模型
function getData(src){
  setTimeout(function(){
    iterator.next({tpl: "tpl.html", name: "fsjohnhuang"})
  }, 1000)
}
function getTpl(tpl){
  setTimeout(function(){
    iterator.next("hello ${name}")
  }, 3000)
}
// 同步任務(wù)
function render(data, tpl){
  return tpl.replace(/${(w+)}/, function(){
    return data[arguments[1]] ==  void 0 ? arguments[0] : data[arguments[1]]
  })
}

// 主邏輯
function *getAritcles(src){
  console.log("begin")
  var data = yield getData(src)
  var tpl = yield getTpl(data.tpl)
  var res = render(data, tpl)
  console.log(rest)
}

主邏輯中異步調(diào)用的寫法與同步調(diào)用的基本沒差異了,爽了吧!但異步任務(wù)模型與生成器函數(shù)及其生成的迭代器耦合性太大,還是不太好用。下面我們通過實(shí)現(xiàn)了Promises/A+規(guī)范的Q來進(jìn)一步解耦。

若執(zhí)行引擎不支持關(guān)鍵字yield,那么上述代碼不就無法執(zhí)行了嗎?還是那句話,yield關(guān)鍵字其實(shí)就是語法糖,最終還是會被解析為一個(gè)迭代器。因此我們自行實(shí)現(xiàn)一個(gè)迭代器也是能實(shí)現(xiàn)上述效果的,不過過程會繁瑣很多(若如第2節(jié)的示例那樣存在try...catch語句,就繁瑣死了@~@),并且代碼的整潔性、可維護(hù)性就全靠攻城獅來保證了。(語法糖從語法層面簡化編程和維護(hù)難度,但理解底層的工作原理也十分重要哦?。?/p> 六、與Q結(jié)合                        

// 異步任務(wù)模型
function getData(src){
  var deferred = Q.defer()
  setTimeout(function(){
   defer.resolve({tpl: "tpl.html", name: "fsjohnhuang"})
  }, 1000)
  return deferred.promise
}
function getTpl(tpl){
  var deferred = Q.defer()
  setTimeout(function(){
   defer.resolve("hello ${name}")
  }, 3000)
  return deferred.promise
}
// 同步任務(wù)
function render(data, tpl){
  return tpl.replace(/${(w+)}/, function(){
    return data[arguments[1]] ==  void 0 ? arguments[0] : data[arguments[1]]
  })
}

// 主邏輯
Q.async(function *(){
  console.log("begin")
  var data = yield getData("dummy.json")
  var tpl = yield getTpl(data.tpl)
  var res = render(data, tpl)
  console.log(rest)
})

暫未閱讀Q的源代碼,暫不作詳細(xì)分析。反正API就這樣用,呵呵!

七、與iPromise結(jié)合                    

iPromise是我開發(fā)的一個(gè)Promises/A+的完整實(shí)現(xiàn),閱讀源碼你會發(fā)現(xiàn)它繼承了jQuery.Deferred1.5~2.1、jsDeferred、mmDeferred和Promises/A官網(wǎng)實(shí)現(xiàn)示例的精妙設(shè)計(jì),并且從v0.0.6開始支持ES6特性GeneratorFunction。使用示例如下:

var getData = function(dataSrc){
  return iPromise(function(r){
    setTimeout(function(){
        r(dataSrc + " has loaded")
    }, 1000)
  })
}
var getTpl = function(tplSrc){
  return iPromise(function(r){
    setTimeout(function(){
        r(tplStr + " has loaded")
    }, 2000)
  })
}
var render = function(data, tpl){
    throw new Error("OMG!")
}

iPromise(function *(dataSrc, tplSrc){
  try{
    var data = yield getData(dataSrc)
    var tpl = yield getTpl(tplSrc)
    render(data, tpl)
  }
  catch(e){
    console.log(e)
  }
  console.log("over!")
}, "dummyData.json", "dummyTpl.json")
/* 結(jié)果如下 */
// 等待1秒多顯示 dummyData.json has loaded
// 等待2秒多顯示 dummyTpl.json has loaded
// 顯示 Error: OMG!
//     Stack trace:
//     test10/render/

v0.6.0的中通過遞歸來實(shí)現(xiàn),具體如下(https://github.com/fsjohnhuang/iPromise/blob/master/src/iPromise.js#L7...):

// FF下生成器函數(shù)的入?yún)⒈仨氃趧?chuàng)建迭代器時(shí)傳遞
// 若第一次調(diào)用迭代器的next函數(shù)傳遞參數(shù),則會報(bào)TypeError: attempt to send 第一個(gè)入?yún)⒅?to newborn generator
var iterator = mixin.apply(null, toArray(arguments,1))
var next = function(){
  var deferred = iPromise()
  deferred.resolve.apply(deferred, arguments)

  return deferred.then(function(){
    var yieldReturn = iterator.next.apply(iterator, arguments)
     if(yieldReturn.done) throw Error("StopIteration")

     return yieldReturn.value
  }).then(next, function(e){
    iterator.throw(e)
  })
}
deferred.resolve()
deferred.then(next)
八、總結(jié)                          

Generator Function并不是為異步編程而生,但可以將它結(jié)合Promise來實(shí)現(xiàn)良好的異步編程模型。本篇內(nèi)容僅簡單介紹Generator Function及相關(guān)的異步編程內(nèi)容,若有紕漏請各位指正,謝謝!

九、 參考                          

http://huangj.in/765
https://www.imququ.com/post/generator-function-in-es6.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/The_Iter...
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Stat...*
http://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html
http://www.cnblogs.com/draem0507/p/3795189.html
http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-i...
http://blog.zhaojie.me/2010/06/code-for-fun-iterator-generator-yield-i...
http://blog.zhaojie.me/2010/07/why-java-sucks-and-csharp-rocks-6-yield...
如果您覺得本文的內(nèi)容有趣就掃一下吧!捐贈互勉!
??

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85835.html

相關(guān)文章

  • 使用JavaScript ES6的新特性計(jì)算Fibonacci(非波拉契數(shù)列)

    摘要:采用的生成非波拉契數(shù)列提供了原生的支持,語法非常有特色,關(guān)鍵字后面緊跟一個(gè)星號。的詳細(xì)介紹參考官網(wǎng)先看如何用這個(gè)黑科技重新實(shí)現(xiàn)非波拉契樹立的生成。在這個(gè)內(nèi)部,我們定義了一個(gè)無限循環(huán),用于計(jì)算非波拉契數(shù)列。 程序員面試系列 Java面試系列-webapp文件夾和WebContent文件夾的區(qū)別? 程序員面試系列:Spring MVC能響應(yīng)HTTP請求的原因? Java程序員面試系列-什么...

    yanbingyun1990 評論0 收藏0
  • ES6 系列之 Babel 將 Generator 編譯成了什么樣子

    摘要:前言本文就是簡單介紹下語法編譯后的代碼。如果有錯誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,請?wù)必給予指正,十分感謝。如果喜歡或者有所啟發(fā),歡迎,對作者也是一種鼓勵。 前言 本文就是簡單介紹下 Generator 語法編譯后的代碼。 Generator function* helloWorldGenerator() { yield hello; yield world; return ending...

    EddieChan 評論0 收藏0
  • javascript中Function、ArrowFunction和GeneratorFunctio

    摘要:等價(jià)與注意如果構(gòu)造函數(shù)有自己的返回,那么情況有所不同。,定義了的屬性,默認(rèn)是聲明的函數(shù)名,匿名函數(shù)是。匿名函數(shù)表達(dá)式和函數(shù)聲明都不會創(chuàng)建匿名作用域。 ECMAScript規(guī)范中對Function的文檔描述,我認(rèn)為是ECMAScript規(guī)范中最復(fù)雜也是最不好理解的一部分,它涉及到了各方面。光對Function就分了Function Definitions、Arrow Function D...

    cyixlq 評論0 收藏0
  • ECMAScript 6新特性印象之一:新語法

    摘要:下例實(shí)現(xiàn)了一個(gè)數(shù)組的迭代器在中,可迭代數(shù)據(jù)結(jié)構(gòu)比如數(shù)組都必須實(shí)現(xiàn)一個(gè)名為的方法,該方法返回一個(gè)該結(jié)構(gòu)元素的迭代器。原話是還可以傳遞返回值。 前記 按照規(guī)劃,明年年中,ECMAScript 6(ES6)就要正式發(fā)布了。 最近抽空看了Dr. Axel Rauschmayer的幾篇文章和演講PPT,對新特性有了些了解。 趁沒忘,抓緊記錄下,夾雜自己的感受。 計(jì)劃分三部分: 新語法...

    馬忠志 評論0 收藏0
  • es6 Generators詳解

    摘要:每個(gè)任務(wù)必須顯式地掛起自己,在任務(wù)切換發(fā)生時(shí)給予它完全的控制。在這些嘗試中,數(shù)據(jù)經(jīng)常在任務(wù)之間共享。但由于明確的暫停,幾乎沒有風(fēng)險(xiǎn)。 翻譯自 github 概述 什么是generators? 我們可以把generators理解成一段可以暫停并重新開始執(zhí)行的函數(shù) function* genFunc() { // (A) console.log(First); yi...

    zhaot 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<