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

資訊專欄INFORMATION COLUMN

深入理解Generator

Euphoria / 531人閱讀

摘要:如果你已經(jīng)理解基礎(chǔ)可以直接跳過這部分和語法部分,直接看深入理解的部分。的參數(shù)可以傳入一個參數(shù),來作為上一次的表達式的返回值,就像我們上面說的會讓等于。

這篇文章旨在幫你真正了解Generator,文章較長,不過如果能花時間耐心看完,相信你已經(jīng)能夠完全理解generator
為什么要用generator

在前端開發(fā)過程中我們經(jīng)常需要先請求后端的數(shù)據(jù),再用拿來的數(shù)據(jù)進行使用網(wǎng)頁頁面渲染等操作,然而請求數(shù)據(jù)是一個異步操作,而我們的頁面渲染又是同步操作,這里ES6中的generator就能發(fā)揮它的作用,使用它可以像寫同步代碼一樣寫異步代碼。下面是一個例子,先忽略下面的寫法,后面會詳細說明。如果你已經(jīng)理解generator基礎(chǔ)可以直接跳過這部分和語法部分,直接看深入理解的部分。

function *foo() {
  // 請求數(shù)據(jù)
  var data = yield makeAjax("http://www.example.com");
  render(data);
}

在等待數(shù)據(jù)的過程中會繼續(xù)執(zhí)行其他部分的代碼,直到數(shù)據(jù)返回才會繼續(xù)執(zhí)行foo中后面的代碼,這是怎么實現(xiàn)的那?我們都知道js是單線程的,就是說我們不可能同時執(zhí)行兩段代碼,要實現(xiàn)這種效果,我們先來猜想下,我們來假設(shè)有一個“王杖”(指代cpu的執(zhí)行權(quán)),誰拿到這個“王杖”,誰就可以做自己想做的事,現(xiàn)在代碼執(zhí)行到foo我們現(xiàn)在拿著“王杖”然后向服務(wù)器請求數(shù)據(jù),現(xiàn)在數(shù)據(jù)還沒有返回,我們不能干等著。作為王我們有著高尚的馬克思主義思想,我們先把自己的權(quán)利交出去,讓下一個需要用的人先用著,當(dāng)然前提是要他們約定好一會兒有需要,再把“王杖”還給我們。等數(shù)據(jù)返回之后,我們再把我們的“王杖”要回來,就可以繼續(xù)做我們想做的事情了。
如果你理解了這個過程,那么恭喜你,你已經(jīng)基本理解了generator的運行機制,我這么比喻雖然有些過程不是很貼切,但基本是這么個思路。更多的東西還是向下看吧。

generator語法 generator函數(shù)

在用generator之前,我們首先要了解它的語法。在上面也看到過,它跟函數(shù)聲明很像,但后面有多了個*號,就是function *foo() { },當(dāng)然也可以這么寫function* foo() { }。這里兩種寫法沒有任何區(qū)別,全看個人習(xí)慣,這篇文章里我會用第一種語法。現(xiàn)在我們按這種語法聲明一個generator函數(shù),供后面使用。

function *foo() {

}
yield

到目前為止,我們還什么也干不了,因為我們還缺少了一個重要的老伙計yield。yield翻譯成漢語是產(chǎn)生的意思。yield會讓我們跟在后面的表達式執(zhí)行,然后交出自己的控制權(quán),停在這里,直到我們調(diào)用next()才會繼續(xù)向下執(zhí)行。這里新出現(xiàn)了next我們先跳過,先說說generator怎么執(zhí)行。先看一個例子。

function *foo() {
  var a = yield 1 + 1;
  var b = yield 2 + a;
  console.log(b);
}

var it = foo();
it.next();
it.next(2);
it.next(4);

下面我們來逐步分析,首先我們定義了一個generator函數(shù)foo,然后我們執(zhí)行它foo(),這里跟普通函數(shù)不同的是,它執(zhí)行完之后返回的是一個迭代器,等著我們自己卻調(diào)用一個又一個的yield。怎么調(diào)用那,這就用到我們前面提到的next了,它能夠讓迭代器一個一個的執(zhí)行。好,現(xiàn)在我們調(diào)用第一個it.next(),函數(shù)會從頭開始執(zhí)行,然后執(zhí)行到了第一個yield,它首先計算了1 + 1,嗯,然后停了下來。然后我們調(diào)用第二個it.next(2),注意我這里傳入了一個2作為next函數(shù)的參數(shù),這個2傳給了a作為它的值,你可能還有很多其他的疑問,我們詳細的后面再說。接著來,我們的it.next(2)執(zhí)行到了第二個yield,并計算了2 + a由于a2所以就變成了2 + 2。第三步我們再調(diào)用it.next(4),過程跟上一步相同,我們把b賦值為4繼續(xù)向下執(zhí)行,執(zhí)行到了最后打印出我們的b4。這就是generator執(zhí)行的全部的過程了?,F(xiàn)在弄明白了yieldnext的作用,回到剛才的問題,你可能要問,為什么要在next中傳入24,這里是為了方便理解,我手動計算了1 + 12 + 2的值,那么程序自己計算的值在哪里?是next函數(shù)的返回值嗎,帶著這個疑問,我們來看下面一部分。

next next的參數(shù)

next可以傳入一個參數(shù),來作為上一次yield的表達式的返回值,就像我們上面說的it.next(2)會讓a等于2。當(dāng)然第一次執(zhí)行next也可以傳入一個參數(shù),但由于它沒有上一次yield所以沒有任何東西能夠接受它,會被忽略掉,所以沒有什么意義。

next的返回值

在這部分我們說說next返回值,廢話不多說,我們先打印出來,看看它到底是什么,你可以自己執(zhí)行一下,也可以直接看我執(zhí)行的結(jié)果。

function *foo() {
  var a = yield 1 + 1;
  var b = yield 2 + a;
  console.log(b);
}

var it = foo();
console.log(it.next());
console.log(it.next(2));
console.log(it.next(4));

執(zhí)行結(jié)果:

{ value: 2, done: false }
{ value: 4, done: false }
4
{ value: undefined, done: true }

看到這里你會發(fā)現(xiàn),yield后面的表達式執(zhí)行的結(jié)果確實返回了,不過是在返回值的value字段中,那還有done字段使用來做什么用的那。其實這里的done是用來指示我們的迭代器,就是例子中的it是否執(zhí)行完了,仔細觀察你會發(fā)現(xiàn)最后一個it.next(4)返回值是done: true的,前面的都是false,那么最后一個打印值的undefined又是什么那,因為我們后面沒有yield了,所以這里沒有被計算出值,那么怎么讓最后一個有值那,很簡單加個return。我們改寫下上面的例子。

function *foo() {
  var a = yield 1 + 1;
  var b = yield 2 + a;
  return b + 1;
}

var it = foo();
console.log(it.next());
console.log(it.next(2));
console.log(it.next(4));

執(zhí)行結(jié)果:

{ value: 2, done: false }
{ value: 4, done: false }
{ value: 5, done: true }

最后的nextvalue的值就是最終return返回的值。到這里我們就不再需要手動計算我們的值了,我們在改寫下我們的例子。

function *foo() {
  var a = yield 1 + 1;
  var b = yield 2 + a;
  return b + 1;
}

var it = foo();
var value1 = it.next().value;
var value2 = it.next(value1).value;
console.log(it.next(value2));

大功告成!這些基本上就完成了generator的基礎(chǔ)部分。但是還有更多深入的東西需要我們進一步挖掘,看下去,相信你會有收獲的。

深入理解

前兩部分我們學(xué)習(xí)了為什么要用generator以及generator的語法,這些都是基礎(chǔ),下面我們來看點不一樣的東西,老規(guī)矩先帶著問題才能更有目的性的看,這里先提出幾個問題:

怎樣在異步代碼中使用,上面的例子都是同步的啊

如果出現(xiàn)錯誤要怎么進行錯誤的處理

一個個調(diào)用next太麻煩了,能不能循環(huán)執(zhí)行或者自動執(zhí)行那

迭代器

進行下面所有的部分之前我們先說一說迭代器,看到現(xiàn)在,我們都知道generator函數(shù)執(zhí)行完返回的是一個迭代器。在ES6中同樣提供了一種新的迭代方式for...offor...of可以幫助我們直接迭代出每個的值,在數(shù)組中它像這樣。

for (var i of ["a", "b", "c"]) {
  console.log(i);
}

// 輸出結(jié)果
// a
// b
// c

下面我們用我們的generator迭代器試試

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

// 獲取迭代器
var it = foo();

for(var i of it) {
  console.log(i);
}

// 輸出結(jié)果
// 1
// 2
// 3

現(xiàn)在我們發(fā)現(xiàn)for...of會直接取出我們每一次計算返回的值,直到done: true。這里注意,我們的4沒有打印出來,說明for...of迭代,是不包括donetrue的時候的值的。

下面我們提一個新的問題,如果在generator中執(zhí)行generator會怎么樣?這里我們先認識一個新的語法yield *,這個語法可以讓我們在yield跟一個generator執(zhí)行器,當(dāng)yield遇到一個新的generator需要執(zhí)行,它會先將這個新的generator執(zhí)行完,再繼續(xù)執(zhí)行我們當(dāng)前的generator。這樣說可能不太好理解,我們看代碼。

function *foo() {
  yield 2;
  yield 3;
  yield 4;
}

function * bar() {
  yield 1;
  yield *foo();
  yield 5;
}

for ( var v of bar()) {
  console.log(v);
}

這里有兩個generator我們在bar中執(zhí)行了foo,我們使用了yield *來執(zhí)行foo,這里的執(zhí)行順序會是yield 1,然后遇到foo進入foo中,繼續(xù)執(zhí)行foo中的yield 2直到foo執(zhí)行完畢。然后繼續(xù)回到bar中執(zhí)行yield 5所以最后的執(zhí)行結(jié)果是:

1
2
3
4
5
異步請求

我們上面的例子一直都是同步的,但實際上我們的應(yīng)用是在異步中,我們現(xiàn)在來看看異步中怎么應(yīng)用。

function request(url) {
  makeAjaxCall(url, function(response) {
    it.next(response);
  })
}

function *foo() {
  var data = yield request("http://api.example.com");
  console.log(JSON.parse(data));
}

var it = foo();
it.next();

這里又回到一開頭說的那個例子,異步請求在執(zhí)行到yield的時候交出控制權(quán),然后等數(shù)據(jù)回調(diào)成功后在回調(diào)中交回控制權(quán)。所以像同步一樣寫異步代碼并不是說真的變同步了,只是異步回調(diào)的過程被封裝了,從外面看不到而已。

錯誤處理

我們都知道在js中我們使用try...catch來處理錯誤,在generator中類似,如果在generator內(nèi)發(fā)生錯誤,如果內(nèi)部能處理,就在內(nèi)部處理,不能處理就繼續(xù)向外冒泡,直到能夠處理錯誤或最后一層。

內(nèi)部處理錯誤:

// 內(nèi)部處理
function *foo() {
  try {
    yield Number(4).toUpperCase();
  } catch(e) {
    console.log("error in");
  }
}

var it = foo();
it.next();

// 運行結(jié)果:error in

外部處理錯誤:

// 外部處理
function *foo() {
  yield Number(4).toUpperCase();
}

var it = foo();
try {
  it.next();
} catch(e) {
  console.log("error out");
}

// 運行結(jié)果:error out

generator的錯誤處理中還有一個特殊的地方,它的迭代器有一個throw方法,能夠?qū)㈠e誤丟回generator中,在它暫停的地方報錯,再往后就跟上面一樣了,如果內(nèi)部能處理則內(nèi)部處理,不能內(nèi)部處理則繼續(xù)冒泡。

內(nèi)部處理結(jié)果:

function *foo() {
  try {
    yield 1;
  } catch(e) {
    console.log("error", e);
  }
  yield 2;
  yield 3;
}

var it = foo();
it.next();
it.throw("oh no!");

// 運行結(jié)果:error oh no!

外部處理結(jié)果:

function *foo() {
  yield 1;
  yield 2;
  yield 3;
}

var it = foo();
it.next();
try {
  it.throw("oh no!");
} catch (e) {
  console.log("error", e);
}

// 運行結(jié)果:error oh no!

根據(jù)測試,發(fā)現(xiàn)迭代器的throw也算作一次迭代,測試代碼如下:

function *foo() {
  try {
    yield 1;
    yield 2;
  } catch (e) {
    console.log("error", e);
  }
  yield 3;
}

var it = foo();
console.log(it.next());
it.throw("oh no!");
console.log(it.next());

// 運行結(jié)果
// { value: 1, done: false }
// error oh no!
// { value: undefined, done: true }

當(dāng)用throw丟回錯誤的時候,除了try中的語句,迭代器迭代掉了yield 3下次再迭代就是,就是最后結(jié)束的值了。錯誤處理到這里就沒有了,就這么點東西^_^。

自動運行

generator能不能自動運行?當(dāng)然能,并且有很多這樣的庫,這里我們先自己實現(xiàn)一個簡單的。

function run(g) {
  var it = g();

  // 利用遞歸進行迭代
  (function iterator(val) {
    var ret = it.next(val);

    // 如果沒有結(jié)束
    if(!ret.done) {
      // 判斷promise
      if(typeof ret.value === "object" && "then" in ret.value) {
        ret.value.then(iterator);
      } else {
        iterator(ret.value);
      }
    }
  })();
}

這樣我們就能自動處理運行我們的generator了,當(dāng)然我們這個很簡單,沒有任何錯誤處理,如何讓多個generator同時運行,這其中涉及到如何進行控制權(quán)的轉(zhuǎn)換問題。我寫了一個簡單的執(zhí)行器Fo,其中包含了Kyle Simpson大神的一個ping-pong的例子,感興趣的可以看下這里是傳送門,當(dāng)然能順手star一下就更好了,see you next article ~O(∩_∩)O~。

參考鏈接

The Basics Of ES6 Generators

Diving Deeper With ES6 Generators

Going Async With ES6 Generators

Getting Concurrent With ES6 Generators

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

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

相關(guān)文章

  • 深入理解 Generator 函數(shù)

    摘要:同時,迭代器有一個方法來向函數(shù)中暫停處拋出一個錯誤,該錯誤依然可以通過函數(shù)內(nèi)部的模塊進行捕獲處理。 本文翻譯自:Diving Deeper With ES6 Generators 由于個人能力有限,翻譯中難免有紕漏和錯誤,望不吝指正issue ES6 Generators:完整系列 The Basics Of ES6 Generators Diving Deeper With E...

    jzzlee 評論0 收藏0
  • es6之深入理解promise

    摘要:形式非必須,也非必須調(diào)用把用函數(shù)表示在調(diào)用的時候用函數(shù)代碼更加同步化三是什么異步操作的終極解決方案寫法四總結(jié)不管用還是用還是用,都保證你寫的的返回值是一個對象 一、promise入門 1. Promise對象是什么 回調(diào)函數(shù)的另一種原生實現(xiàn),比之前回調(diào)函數(shù)的寫法機構(gòu)清晰,功能強大, 2.以前回調(diào)這么寫 function a(fn){ let h = 1; setTime...

    luckyw 評論0 收藏0
  • ES6-7

    摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關(guān)于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...

    mudiyouyou 評論0 收藏0
  • ES6深入淺出 Generator

    摘要:一步,一步前進一步深入淺出之生成器。本人對生成器的印象是語法難以理解,又沒有什么實際的應(yīng)用場景。為啥要學(xué)習(xí)一下呢可能未來某些高級的業(yè)務(wù)會用到,還有萬一面試官問的話,我得能侃幾句,顯得我牛 一步,一步前進の一步 ES6深入淺出之Generator生成器。本人對生成器的印象是語法難以理解,又沒有什么實際的應(yīng)用場景。為啥要學(xué)習(xí)一下呢?可能未來某些高級的業(yè)務(wù)會用到,還有萬一面試官問的話,我得能...

    DobbyKim 評論0 收藏0
  • JavaScript 異步

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...

    tuniutech 評論0 收藏0

發(fā)表評論

0條評論

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