最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文了解你不知道的JavaScript生成器篇

 更新時間:2022年11月09日 14:16:33   作者:霍格沃茨魔法師  
ES6引入了一個新的函數(shù)類型,發(fā)現(xiàn)它并不符合這種運行到結(jié)束的特性。這類新的函數(shù)被稱為生成器。生成器的出現(xiàn)是我們知道原來有時代碼并不會順利的運行,可以通過暫停的方式進行異步回調(diào),讓我們摒棄了此前的認知。本文就來聊聊JavaScript中生成器的相關知識

前言

在沒有JavaScript的生成器概念之前,我們幾乎普遍依賴一個假定:一個函數(shù)一旦開始執(zhí)行,就會運行到結(jié)束,期間不會有其他代碼能夠打斷它并插入其間。如下代碼所示:

 var x = 1;
 function foo(){
  x++;
  bar();
  console.log("x",x);
}
 function bar(){
  x++;
}
 foo();  //x:3

不過直到ES6引入了一個新的函數(shù)類型,發(fā)現(xiàn)它并不符合這種運行到結(jié)束的特性。這類新的函數(shù)被稱為生成器。生成器的出現(xiàn)是我們知道原來有時代碼并不會順利的運行,可以通過暫停的方式進行異步回調(diào),讓我們摒棄了此前的認知。

了解生成器

下面來看一段合作式并發(fā)的ES6代碼:

var x = 1;
function *foo(){
   x++;
   yield;//暫停
   console.log("x",x)
}
function bar(){
    x++;
}

可以看到使用了*foo的形式生成這個函數(shù),代表生成器而非常規(guī)函數(shù)。

現(xiàn)在,我們要如何運行前面的代碼片段,使得bar()在*foo()內(nèi)部的yield處執(zhí)行呢?

步驟如下:

(1) 首先var it = foo() 構(gòu)造一個迭代器it來控制這個生成器,這個迭代器會控制它的執(zhí)行。

(2) 使用it.next() 啟動生成器*foo(),并運行了*foo()第一行的x++。

(3) *foo() 在yield語句處暫停,在這一點上使得第一個it.next()調(diào)用結(jié)束。此時*foo()仍在運行并且是活躍的,但處于暫停狀態(tài)。

(4) 此刻我們查看x的值,此時為2

(5) 然后我們調(diào)用bar(),它通過x++再次遞增x。

(6) 此刻我們再次查看x的值,此時為3。

(7) 最后再次調(diào)用it.next()調(diào)用從暫停處恢復了生成器*foo()的執(zhí)行,并運行console.log(..)語句,這條語句使用當前的值為3.

顯然,foo()啟動了,但是并沒有完整運行,它在yield處暫停了。后面恢復了foo()并讓它運行到結(jié)束,但這不是必須的。

因此,生成器就是一類特殊的函數(shù),可以一次或多次啟動和停止,并不一定非得要完成。盡管現(xiàn)在還不是特別清楚它的強大之處,但往后我們會看到它將成為構(gòu)件以生成器作為異步流程控制的代碼模式的基礎構(gòu)建之一。

對于生成器函數(shù)是一個特殊的函數(shù)這個概念,看兩個例子來更深入的理解一下:

代碼1

function *foo(x,y){
   return x*y;
}
var it = foo(6,7);
var res = it.next();
res.value; //42

代碼2

function *foo(x){
  var y = x *(yield);
  return y;
}
var it = foo(6);
//啟動foo()
it.next();
var res = it.next(7);
res.value //輸出什么?

通過對比兩個代碼其實可以發(fā)現(xiàn)它的相似之處。我們主要分析第二個代碼。首先,傳入6作為參數(shù)x。然后調(diào)用it.next(),這會啟動foo().在foo()內(nèi)部,開始執(zhí)行語句var y = x...,但隨后就遇到了yield表達式。它很神奇的就會在這一點上暫停*foo(),并在本質(zhì)上要求調(diào)用代碼為yield表達式提供一個結(jié)果值。接下來,調(diào)用it.next(7),這一句把值傳回作為被暫停的yield表達式的結(jié)果。所以,此時的賦值語句為var y = 6 * 7,現(xiàn)在return這個42作為it.next(7)的結(jié)果。

實際上我們考慮的重點是這段代碼中的這兩行:

var y = x * (yield);
return y;

這段代碼,在第一個yield這里應該插入什么值呢?由于第一個next()運行,使得生成器啟動并運行到此處,所以顯然他無法回答這個問題,那么第二次next()調(diào)用回答第一個yield提出的這個問題,傳入了7。

注意,是第二個next回答第一個yield;

再把代碼稍微改動一下:

function *foo(x){
  var y = x *(yield “hello”);
  return y;
}
var it = foo(6);
//啟動foo()
var res = it.next();
res.value //輸出什么?
res = it.next(7);
res.value //輸出什么?

在第一次調(diào)用next之后,沒有傳入任何東西,res.value的值是hello,第二次向上一步暫停的yield處傳入7,于是開始了6*7的計算,res.value的值變?yōu)?2.

這里的每一個next都得到了回應。

小記:在第一次next()調(diào)用時沒有傳入任何值,此時的value就是yield后的數(shù)據(jù),第二次向next()傳入?yún)?shù)之后把這個參數(shù)代入yield處。其實呢,yield和next()這一對組合起來,在生成器的執(zhí)行過程中構(gòu)建了一個雙向消息傳遞系統(tǒng)。我們并沒有向第一個next()調(diào)用發(fā)送值,這是有意為之,只有暫停的yield才能接收這樣一個通過next()傳遞的值,而在生成器的起始處我們調(diào)用第一個next()時,還沒有暫停的yield來接收這樣的一個值,所以不要在第一個next()上傳遞參數(shù)。

for...of

就像ES6新增的for...of循環(huán)一樣,這意味著可以通過原生循環(huán)語法自動迭代標準迭代器:

var a = [1,3,5,7,9]
for(var v of a){
    console.log(v); //1 3 5 7 9
}

for...of循環(huán)在每次迭代中自動調(diào)用next(),他不會向next()傳入任何值,并且會在接收到done:true之后手動停止,這對于在一組數(shù)據(jù)上循環(huán)很方便。循環(huán)向a請求它的迭代器,并自動使用這個迭代器迭代遍歷a的值。

iterable(可迭代)

從ES6開始,從一個iterable中提取迭代器的方法是:iterable必須支持一個函數(shù),其名稱是專門的ES6符號值Symbol.iterator。調(diào)用這個函數(shù)時,它會返回一個迭代器,通常每次調(diào)用會返回一個全新的迭代器,雖然這一點并不是必須的。就像前面使用for...of直接迭代的一樣,我們使用迭代器重寫:

var a = [1,3,5,7,9]
var it = a[Symbol.iterator]()
it.next().value;//1
it.next().value;//3
it.next().value;//5

生成器+promise

ES6中最完美的世界就是生成器和promise的結(jié)合。但如何實現(xiàn)呢?

讓我們來試一下,把支持promise的foo()和生成器*main()放在一起:

function foo(x,y){
    return request(
      "http:url/?x+y"
    )
}
function *main(){
    try{
       var text = yield foo(1,2)
       console.log(text)
    }
    catch(err){
        console.error(err)
    }
}

現(xiàn)在如何運行*main()呢?還有一些實現(xiàn)細節(jié)需要補充,來實現(xiàn)接收和連接yield出來的promise,使它能夠在決議之后恢復生成器,先從手工開始實現(xiàn):

var it = main()
var p = it.next().value //此時p為foo(1,2)
p.then( //等待promise的p決定成功/拒絕
  function(){
    it.next(text)
  },
  function(err){
    it.throw(err)
  }
)

這個模式下生成器yield出promise,然后其控制生成器的迭代器來執(zhí)行它,直到結(jié)束,是非常強大有用的一種方法。對于ES7中,在這一方面增加語法支持的提案已經(jīng)有了一些很強勢的支持。

async與await

function foo(){
   return request(
      "http:url/?x+y"
   )
}
async function main(){
  try{
     var text = await foo(1,2)
     console.log(text)
  }
  catch(err){
    console.log(err);
  }
}
main();

可以看到main不再被聲明為*main生成器函數(shù),它現(xiàn)在是一類新的函數(shù):async函數(shù),并且我們也不用yield暫停點來暫停等待了,而是使用await等待并決議。我們await了一個promise,async函數(shù)就會自動獲知要做什么,它會暫停這個函數(shù)(就像yield),直到promise生成成功/拒絕的結(jié)果。

小結(jié)

生成器是ES6的一個新的函數(shù)類型,它并不像普通函數(shù)那樣總是從運行開始到運行結(jié)束。取而代之的是,生成器yield可以在運行當中暫停,并且等到將來再次next()時再從暫停的地方恢復運行。

這種交替的暫停和恢復是合作式的雙向消息傳遞,這意味著生成器具有獨一無二的能力來暫停自身,這是通過關鍵字yield實現(xiàn)的。不過,只有控制生成器的迭代器具有恢復生成器的功能(比如next())

yield和next()這一對不只是一種控制機制,實際上也是一種雙向消息傳遞機制。yield..表達式本質(zhì)是暫停下來等待某個值,接下來的next()調(diào)用會向被暫停的yield表達式傳回一個值(或者是隱式的undefined)

有時,我們還會把可能的異步藏在yield后面,把異步移動到控制生成器的迭代器的代碼部分,如yield foo(1,2)。換句話說,生成器為異步代碼保持了順序、同步、阻塞的代碼模式,這使得大腦可以更自然地追蹤代碼,解決了基于回調(diào)的異步的缺陷。

以上就是一文了解你不知道的JavaScript生成器篇的詳細內(nèi)容,更多關于JavaScript生成器的資料請關注腳本之家其它相關文章!

相關文章

  • Web前端開發(fā)之水印、圖片驗證碼

    Web前端開發(fā)之水印、圖片驗證碼

    這篇文章主要介紹了Web前端開發(fā)之水印、圖片驗證碼的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-11-11
  • js實現(xiàn)仿QQ秀換裝效果的方法

    js實現(xiàn)仿QQ秀換裝效果的方法

    這篇文章主要介紹了js實現(xiàn)仿QQ秀換裝效果的方法,實例分析了javascript操作圖片的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-03-03
  • layui文件上傳控件帶更改后數(shù)據(jù)傳值的方法

    layui文件上傳控件帶更改后數(shù)據(jù)傳值的方法

    今天小編就為大家分享一篇layui文件上傳控件帶更改后數(shù)據(jù)傳值的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • 小程序?qū)崿F(xiàn)上下切換位置

    小程序?qū)崿F(xiàn)上下切換位置

    這篇文章主要為大家詳細介紹了小程序?qū)崿F(xiàn)上下切換位置,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • JavaScript去掉數(shù)組中的重復元素

    JavaScript去掉數(shù)組中的重復元素

    在寫程序過程中,經(jīng)常會遇到去除數(shù)組中重復元素的需求。要實現(xiàn)這個功能其實并不難
    2011-01-01
  • JSON獲取屬性值方法代碼實例

    JSON獲取屬性值方法代碼實例

    這篇文章主要介紹了JSON獲取屬性值方法代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • JavaScript觸發(fā)器詳解

    JavaScript觸發(fā)器詳解

    JavaScript觸發(fā)器詳解...
    2007-03-03
  • html a標簽-超鏈接中confirm方法使用介紹

    html a標簽-超鏈接中confirm方法使用介紹

    confirm可以彈出確定取消對話框,然后根據(jù)用戶的選擇執(zhí)行相應的操作,接下來介紹實現(xiàn)過程,需要了解的朋友可以參考下
    2013-01-01
  • 小程序webview內(nèi)網(wǎng)頁實現(xiàn)微信支付的代碼示例

    小程序webview內(nèi)網(wǎng)頁實現(xiàn)微信支付的代碼示例

    這篇文章主要介紹了在小程序中使用webview實現(xiàn)微信支付功能,需要在小程序和webview之間進行交互,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2025-02-02
  • Postman的下載及安裝教程詳解

    Postman的下載及安裝教程詳解

    這篇文章主要介紹了Postman的下載及安裝教程詳解,今天給大家介紹的這款網(wǎng)頁調(diào)試工具不僅可以調(diào)試簡單的css、html、腳本等簡單的網(wǎng)頁基本信息,它還可以發(fā)送幾乎所有類型的HTTP請求!需要的朋友可以參考下
    2018-10-10

最新評論

民丰县| 徐水县| 临夏县| 电白县| 定兴县| 图们市| 尤溪县| 阳山县| 天柱县| 巫溪县| 横山县| 康马县| 精河县| 会宁县| 大化| 福海县| 甘肃省| 镇远县| 唐河县| 松阳县| 华池县| 耒阳市| 永济市| 普格县| 江源县| 巴中市| 呼伦贝尔市| 高青县| 积石山| 汤阴县| 娱乐| 平江县| 峨边| 安庆市| 全州县| 甘泉县| 墨江| 新乡市| 海口市| 宁陕县| 巩留县|