亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

異步編程之Generator(2)——剖析特性

系統 2117 0

異步編程系列教程:

  1. (翻譯)異步編程之Promise(1)——初見魅力
  2. 異步編程之Promise(2):探究原理
  3. 異步編程之Promise(3):拓展進階
  4. 異步編程之Generator(1)——領略魅力
  5. 異步編程之Generator(2)——剖析特性
  6. 異步編程之co——源碼分析

Generator基礎


繼上一篇見識過其配合promise帶來的超爽的異步編程體驗,我想應該大部分同學都會想好好看一下,到底這個Generator是什么?接下來我們會對Generator的特性進行剖析,讓我們對接下來學習 co 源碼打個扎實的基礎。

起源

我們首先得知道,Generator一開始并不是用來做異步編程的,是后來的大牛們挖掘了它的特性,讓它在異步編程里大放異彩。其實Generator是生成遍歷器的構造器,ES6定義了一個遍歷器的接口Iterator。任何數據結構滿足Iterator接口,都可以統一實現遍歷操作。一步一步的調用 next() 或者 for..of 循環都可以遍歷實現Iterator接口的數據結構。

我們簡單說一下遍歷對象的 next() 是怎樣的:

  1. 第一次調用 next() 會直接指向第一個數據的位置,然后返回數據的信息。結構是這樣的: {value: AnyType, done: Boolean} value 屬性是指該數據的值, done 則是標志是否已經true,結束了。
  2. 再一次調用 next() 則指向下一個數據,返回相應的數據信息。
  3. 重復第二步,一直到數據結束,返回 {value: undefined, done: true} 。則表示遍歷已經全部完成。

這就是Iterator最基本的實現,當然這里是很片面的,若要展開說,基本又是一大篇文章可以寫。這里就直接給出阮一峰老師關于Iterator的文章: 10. Iterator和for...of循環

定義

在我們知道了Generator生成的遍歷對象是什么之后,我們看一下如何定義這樣的Generator函數。對上一篇有印象的同學,應該記得函數標識符后面有一個詭異的星號 function* () 。其實這個星號在括號前也是沒關系的,這里我是參考了 co 源碼的。我們一旦定義了一個帶星號的函數之后,用這個構造器生成的對象在harmony模式里就成了Generator對象(下面我會稱其為遍歷器)。我們可以測試一下一段代碼。

    
      var toString = Object.prototype.toString;

var Generator = function* (){
    yield "hello";
    yield "world";
};

var gen = Generator(); // 可以省去new來創建對象

console.log(toString.call(Generator)); // [object Function]
console.log(toString.call(gen));       // [object Generator]
    
  

這樣我們通過調用特殊定義的Generator構造器,生成一個遍歷器([object Generator])。那我們要遍歷的話必須得知道遍歷的每個成員, yield 就是用來定義遍歷成員的。也就是說,遍歷器進行遍歷的時候會以 yield 為間隔,一個 yield 一個成員,不斷往下走直到不存在下一個 yield

在上面的例子中,就是第一次遍歷到 yield 得到"hello",第二次繼續執行遍歷操作到 yield 得到"world",最后再執行就發現沒有了,也就是 done: true 結束遍歷。

接下來我們會詳細說一下,遍歷器是遍歷的各種特性。

Generator特性


遍歷

我們需要執行遍歷,首先就是要得到遍歷器。前面也說過了,就是調用Generator構造器生成的。然后該遍歷器會有一個方法 next() 用來進行遍歷操作,并且每一次的操作都會在 yield 處停止,并等待下一次的 next() 指令。我們看一看剛才的代碼:

    
      var Generator = function* (){
    yield "hello";
    yield "world";
};

var gen = new Generator();

console.log(gen.next()); // { value: 'hello', done: false }
console.log(gen.next()); // { value: 'world', done: false }
console.log(gen.next()); // { value: undefined, done: true }
    
  

我們可以看到最后當 done: true 時, value 是undefined。其實我們return出去一個值,就會成為該 value 的值。其實換一個角度更加有意思,就是當你return出一個值,這個值必定是 done: true 。我們可以改一下上面的例子:

    
      var Generator = function* (){
    yield "hello";
    return "world";
    yield "!";
};

var gen = new Generator();

console.log(gen.next()); // { value: 'hello', done: false }
console.log(gen.next()); // { value: 'world', done: true }
console.log(gen.next()); // { value: undefined, done: true }
    
  

我們可以看到,如果遍歷器去找感嘆號的 yield 話,應該是 value: '!' 。但是因為提前return結束了遍歷器,所以最后得到了 { value: 'world', done: true }

yield傳值

我們知道了每一次遍歷器執行到 yield 處后,會把值放在一個對象中的屬性中返回出去。但是我們在Generator構造器里怎么利用這個值呢?其實我們可以為遍歷器的 next(res) 傳入一個參數,這個參數將會成為這一次 yield 的值。乍一看,好像不大清楚,看看代碼就懂了。

    
      var Generator = function* (){
    var hello = yield "hello";
    console.log(hello);           // hi
    var world = yield "world";
    console.log(world);           // undefined
};

var gen = new Generator();

var first = gen.next("nothing");
var second = gen.next("hi");
var third = gen.next();
    
  

我們第一次 next() 相當于啟動器,這個時候傳入任何參數都是被忽略的,因為這個參數無法作為上一個 yield 的值(沒有上一個)。到我們第二次的 next("hi") ,傳入了一個"hi"字符串,這個參數就成為了 yield 的值,直接賦值給hello變量并打印出來。我們最后一個world變量是undefined,是因為 next() 并沒有傳入任何參數。可以這么說,每一次遍歷器遍歷得到的成員的值,和 yield 的值是沒有必然聯系的。

所以我們看代碼的執行順序也是很有趣的一件事,遍歷器會執行到語句 yield 右側即停止。等到下一次 next() 啟動,然后才會根據 yield 得到的值,對語句左側變量進行賦值。這樣想的話,如果我們下一次 yield 語句,依賴第一次的值,我們就需要在 next() 里傳入上一次的 value 。我們對上一次的代碼做個小小的添加。

    
      var first = gen.next("nothing");
var second = gen.next("hi");
var third = gen.next(second.value); //構造函數的world變量值也會是"hi"。
    
  

這個是Generator非常重要的特性,下去要好好實踐一番,加深印象。接下來 co 源碼分析,這個特性配合promise可以放華麗的大招。

遍歷遍歷器里的遍歷器

我起這個標題挺有意思的,哈哈哈。其實就和遞歸棧差不多,也就是說,當 yield 的是另一個遍歷器,那么代碼會進入到另一個遍歷器里,直到結束后,才交回代碼控制權。看一看咯:

    
      var Generator = function* (){
    yield  "hello";
    yield *anotherGen;
    yield "world";
    return "hello world";
};

var AnotherGenerator = function* (){
    yield "強勢插入!";
    yield "不給hello world!";
}

var gen = new Generator();
var anotherGen = new AnotherGenerator();

console.log(gen.next()); // { value: 'hello', done: false }
console.log(gen.next()); // { value: '強勢插入!', done: false }
console.log(gen.next()); // { value: '不給hello world!', done: false }
console.log(gen.next()); // { value: 'world', done: false }
console.log(gen.next()); // { value: 'hello world', done: true }
    
  

當我們需要遍歷一個遍歷器,那么 * 也是需要的,可以參考一下上面。

總結


我們知道了遍歷對象遍歷時得到的什么,還有 next(res) 傳入參數有什么用,這對接下來的分析有著至關重要的作用。到這里,對Generator分析已經是差不多了。如果想要更深入了解的,可以去阮老師的博客看一看: 11. Generator函數

接下來一篇文章就是對 co 源碼的分析,先預習和復習一些東西吧。我們回顧一下promise,我們在將一個異步操作promise化后,當我們調用這個異步操作,我們會得到一個promise對象。所以我們可以想象一下:

  1. 我們調用遍歷器的 next() 得到該異步的promise對象
  2. 在promise對象的 then() 中的 resolve 對數據進行處理
  3. 把數據作為參數傳入 next(res) ,進行下一次異步操作
  4. 直到迭代器的 done: true ,結束遍歷。

這樣我們就可以一環扣一環的將Generator函數里的異步操作進行迭代,形成一種異步編程同步寫法的優良體驗。當然我們這里不會詳細說,如何去實現,因為我會在下一篇好好講講。

異步編程之Generator(2)——剖析特性


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日韩在线观看一区二区三区 | 黑人边吃奶边扎下面激情视频 | 成人最新午夜免费视频 | 亚洲国产精品成人综合久久久 | 色淫综合 | 亚洲精品字幕一区二区三区 | 天天摸天天草 | 久久国产精品ww | 久久天天躁狠狠躁夜夜不卡 | 亚洲一区二区三区免费视频 | 国产欧美久久久另类精品 | 天堂成人av | 欧美福利视频在线 | 毛片免费毛片一级jjj毛片 | 国产精品第1页 | 中国特级黄一级真人毛片 | 欧美video巨大粗暴18 | 91模特| 久操视频在线观看 | 婷婷国产成人久久精品激情 | 欧美一级毛片免费播放aa | 九九热精彩视频 | 欧美成人性做爰 | 成人网18免费视频 | 99热这里只有免费国产精品 | 95视频在线观看在线分类h片 | 久久精品成人欧美大片免费 | 亚洲图片色图 | 国产一区 在线视频 | 俺去也理论网站 | 四虎影| 99免费视频观看 | 97在线视频免费 | 国产一区二区三区亚洲欧美 | 综合亚洲精品一区二区三区 | 久久亚洲福利 | 激情综合在线 | 国产级a爱做片免费观看 | 欧美成人伊人十综合色 | 全黄h全肉边做边吃奶在线观看 | 日日添日日摸 |