欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

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

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

了解生成器

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

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

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

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

步驟如下:

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

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

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

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

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

(6) 此刻我們?cè)俅尾榭磝的值,此時(shí)為3。

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

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

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

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

代碼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);
//啟動(dòng)foo()
it.next();
var res = it.next(7);
res.value //輸出什么?

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

實(shí)際上我們考慮的重點(diǎn)是這段代碼中的這兩行:

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

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

注意,是第二個(gè)next回答第一個(gè)yield;

再把代碼稍微改動(dòng)一下:

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

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

這里的每一個(gè)next都得到了回應(yīng)。

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

for...of

就像ES6新增的for...of循環(huán)一樣,這意味著可以通過(guò)原生循環(huán)語(yǔ)法自動(dòng)迭代標(biāo)準(zhǔ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)在每次迭代中自動(dòng)調(diào)用next(),他不會(huì)向next()傳入任何值,并且會(huì)在接收到done:true之后手動(dòng)停止,這對(duì)于在一組數(shù)據(jù)上循環(huán)很方便。循環(huán)向a請(qǐng)求它的迭代器,并自動(dòng)使用這個(gè)迭代器迭代遍歷a的值。

iterable(可迭代)

從ES6開(kāi)始,從一個(gè)iterable中提取迭代器的方法是:iterable必須支持一個(gè)函數(shù),其名稱是專門的ES6符號(hào)值Symbol.iterator。調(diào)用這個(gè)函數(shù)時(shí),它會(huì)返回一個(gè)迭代器,通常每次調(diào)用會(huì)返回一個(gè)全新的迭代器,雖然這一點(diǎn)并不是必須的。就像前面使用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é)合。但如何實(shí)現(xiàn)呢?

讓我們來(lái)試一下,把支持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)在如何運(yùn)行*main()呢?還有一些實(shí)現(xiàn)細(xì)節(jié)需要補(bǔ)充,來(lái)實(shí)現(xiàn)接收和連接yield出來(lái)的promise,使它能夠在決議之后恢復(fù)生成器,先從手工開(kāi)始實(shí)現(xiàn):

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

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

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暫停點(diǎn)來(lái)暫停等待了,而是使用await等待并決議。我們await了一個(gè)promise,async函數(shù)就會(huì)自動(dòng)獲知要做什么,它會(huì)暫停這個(gè)函數(shù)(就像yield),直到promise生成成功/拒絕的結(jié)果。

小結(jié)

生成器是ES6的一個(gè)新的函數(shù)類型,它并不像普通函數(shù)那樣總是從運(yùn)行開(kāi)始到運(yùn)行結(jié)束。取而代之的是,生成器yield可以在運(yùn)行當(dāng)中暫停,并且等到將來(lái)再次next()時(shí)再?gòu)臅和5牡胤交謴?fù)運(yùn)行。

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

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

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

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

相關(guān)文章

  • 詳解JavaScript自定義函數(shù)

    詳解JavaScript自定義函數(shù)

    這篇文章主要介紹了JavaScript自定義函數(shù)的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • web-view內(nèi)嵌H5與uniapp數(shù)據(jù)的實(shí)時(shí)傳遞解決方案

    web-view內(nèi)嵌H5與uniapp數(shù)據(jù)的實(shí)時(shí)傳遞解決方案

    這篇文章主要介紹了web-view內(nèi)嵌H5與uniapp數(shù)據(jù)的實(shí)時(shí)傳遞,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-07-07
  • js點(diǎn)擊返回跳轉(zhuǎn)到指定頁(yè)面實(shí)現(xiàn)過(guò)程

    js點(diǎn)擊返回跳轉(zhuǎn)到指定頁(yè)面實(shí)現(xiàn)過(guò)程

    這篇文章主要為大家詳細(xì)介紹了js點(diǎn)擊返回跳轉(zhuǎn)到指定頁(yè)面實(shí)現(xiàn)過(guò)程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • javascript實(shí)現(xiàn)手動(dòng)點(diǎn)贊效果

    javascript實(shí)現(xiàn)手動(dòng)點(diǎn)贊效果

    這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)手動(dòng)點(diǎn)贊效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 一篇文章帶你從零快速上手Rollup

    一篇文章帶你從零快速上手Rollup

    這篇文章主要給大家介紹了如何通過(guò)一篇文章快速?gòu)牧憧焖偕鲜諶ollup的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • 記錄一次websocket封裝的過(guò)程

    記錄一次websocket封裝的過(guò)程

    這篇文章主要介紹了記錄一次websocket封裝的過(guò)程,幫助大家更好的理解和封裝websocket,感興趣的朋友可以了解下
    2020-11-11
  • 微信小程序頁(yè)面間跳轉(zhuǎn)傳參方式總結(jié)

    微信小程序頁(yè)面間跳轉(zhuǎn)傳參方式總結(jié)

    這篇文章主要給大家總結(jié)介紹了關(guān)于微信小程序頁(yè)面間跳轉(zhuǎn)傳參方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用小程序具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • XP折疊菜單&仿QQ2006菜單

    XP折疊菜單&仿QQ2006菜單

    XP折疊菜單&仿QQ2006菜單...
    2006-12-12
  • webpack構(gòu)建換膚功能的思路詳解

    webpack構(gòu)建換膚功能的思路詳解

    這篇文章主要介紹了webpack構(gòu)建下?lián)Q膚功能的思路詳解,需要的朋友可以參考下
    2017-11-11
  • javascript 學(xué)習(xí)筆記(onchange等)

    javascript 學(xué)習(xí)筆記(onchange等)

    javascript 學(xué)習(xí)筆記,一些簡(jiǎn)單的小技巧,學(xué)習(xí)js的朋友可以看下。
    2010-11-11

最新評(píng)論