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

資訊專欄INFORMATION COLUMN

es6 Generators詳解

zhaot / 3361人閱讀

摘要:每個(gè)任務(wù)必須顯式地掛起自己,在任務(wù)切換發(fā)生時(shí)給予它完全的控制。在這些嘗試中,數(shù)據(jù)經(jīng)常在任務(wù)之間共享。但由于明確的暫停,幾乎沒(méi)有風(fēng)險(xiǎn)。

翻譯自

github

概述

什么是generators?

我們可以把generators理解成一段可以暫停并重新開(kāi)始執(zhí)行的函數(shù)

function* genFunc() {
    // (A)
    console.log("First");
    yield; //(B)
    console.log("Second"); //(C)
}

function*是定義generator函數(shù)的關(guān)鍵字,yield是一個(gè)操作符,generator 可以通過(guò)yield暫停自己執(zhí)行,另外,generator可以通過(guò)yield接受輸入和對(duì)外輸入

當(dāng)我們調(diào)用genFunc(),我們得到一個(gè)generator對(duì)象genObj,我們可以通過(guò)這個(gè)genObj控制程序的執(zhí)行

const genObj = genFunc()

上面的程序初始會(huì)暫停在行A,調(diào)用genObj.next()會(huì)使程序繼續(xù)執(zhí)行直到遇到下一個(gè)yield

> genObj.next();
First
{ value: undefined, done: false }

這里先忽略genObj.next()返回的對(duì)象,之后會(huì)介紹

現(xiàn)在,程序暫停在了行B,再次調(diào)用 genObj.next(),程序又開(kāi)始執(zhí)行,行C被執(zhí)行

> genObj.next()
Second
{ value: undefined, done: true }

然后,函數(shù)就執(zhí)行結(jié)束了,再次調(diào)用genObj.next()也不會(huì)有什么效果了

generator能扮演的角色

generators 可以扮演三種角色

迭代器(數(shù)據(jù)生產(chǎn)者)

每一個(gè)yield可以通過(guò)next()返回一個(gè)值,這意味著generators可以通過(guò)循環(huán)或遞歸生產(chǎn)一系列的值,因?yàn)間enerator對(duì)象實(shí)現(xiàn)了Iterable接口,generator生產(chǎn)的一系列值可以被ES6中任意支持可迭代對(duì)象的結(jié)構(gòu)處理,兩個(gè)例子,for of循環(huán)和擴(kuò)展操作(...)

觀察者(數(shù)據(jù)消費(fèi)者)

yield可以通過(guò)next()接受一個(gè)值,這意味著generator變成了一個(gè)暫停執(zhí)行的數(shù)據(jù)消費(fèi)者直到通過(guò)next()給generator傳遞了一個(gè)新值

協(xié)作程序(數(shù)據(jù)生產(chǎn)者和消費(fèi)者)

考慮到generators是可以暫停的并且可以同時(shí)作為數(shù)據(jù)生產(chǎn)者和消費(fèi)者,不會(huì)做太多的工作就可以把generator轉(zhuǎn)變成協(xié)作程序(合作進(jìn)行的多任務(wù))

下面詳細(xì)介紹這三種

generators作為數(shù)據(jù)生產(chǎn)者(iterators)

generators同時(shí)實(shí)現(xiàn)了接口Iterable 和 Iterator(如下所示),這意味著,generator函數(shù)返回的對(duì)象是一個(gè)迭代器也是一個(gè)可迭代的對(duì)象

interface Iterable {
    [Symbol.iterator]() : Iterator;
}
interface Iterator {
    next() : IteratorResult;
}
interface IteratorResult {
    value : any;
    done : boolean;
}

generator對(duì)象完整的接口后面會(huì)提到,這里刪掉了接口Iterable的return()方法,因?yàn)檫@個(gè)方法這一小節(jié)用不到

generator函數(shù)通過(guò)yield生產(chǎn)一系列的值,這些值可以通過(guò)迭代器的next()方法來(lái)使用,例如下面的generator函數(shù)生成了值a和b

function* genFunc(){
    yield "a"
    yield "b"
}

交互展示如下

> const genObj = genFunc();
> genObj.next()
{ value: "a", done: false }

> genObj.next()
{ value: "b", done: false }

> genObj.next() // done: true => end of sequence
{ value: undefined, done: true }

迭代generator的三種方式

for of循環(huán)

   for (const x of genFunc()) {
       console.log(x);
   }
   // Output:
   // a
   // b

擴(kuò)展操作符(...)

const arr = [...genFunc()]; // ["a", "b"]

解構(gòu)賦值

> const [x, y] = genFunc();
> x
"a"
> y
"b"

generator中的return

上面的generator函數(shù)沒(méi)有包含一個(gè)顯式的return,一個(gè)隱式的return 返回undefined,讓我們?cè)囼?yàn)一個(gè)顯式返回return的generator

function* genFuncWithReturn() {
    yield "a";
    yield "b";
    return "result";
}

下面的結(jié)構(gòu)表明return 指定的值保存在最后一個(gè)next()返回的對(duì)象中

> const genObjWithReturn = genFuncWithReturn();
> genObjWithReturn.next()
{ value: "a", done: false }
> genObjWithReturn.next()
{ value: "b", done: false }
> genObjWithReturn.next()
{ value: "result", done: true }

然而,大部分和可迭代對(duì)象一起工作的結(jié)構(gòu)會(huì)忽略done屬性是true的對(duì)象的value值

for (const x of genFuncWithReturn()) {
    console.log(x);
}
// Output:
// a
// b

const arr = [...genFuncWithReturn()]; // ["a", "b"]

yield*會(huì)考慮done屬性為true的value值,后面會(huì)介紹

generator函數(shù)中拋異常

如果一個(gè)異常離開(kāi)了generator函數(shù),next()可以拋出它

function* genFunc() {
    throw new Error("Problem!");
}
const genObj = genFunc();
genObj.next(); // Error: Problem!

這意味著next()可以生產(chǎn)三種類型的值

對(duì)于可迭代序列中的一項(xiàng)x,它返回 {value:x,done:false}

對(duì)于可迭代序列的最后一項(xiàng),明確是return返回的z,它返回{value:z,done:true}

對(duì)于異常,它拋出這個(gè)異常

通過(guò) yield*遞歸

我們只能在generator函數(shù)中使用yield,如果我們想通過(guò)generator實(shí)現(xiàn)遞歸算法,我們就需要一種方式來(lái)在一個(gè)generator中調(diào)用另一個(gè)generator,這就用到了yield*,現(xiàn)在,我們只介紹yield*用在generator函數(shù)產(chǎn)生值的情況,之后介紹yield*用在generator接受值的情況

generator遞歸調(diào)用另一個(gè)generator的方式

function* foo() {
    yield "a";
    yield "b";
}

function* bar() {
    yield "x";
    yield* foo();
    yield "y";
}

執(zhí)行結(jié)構(gòu)

const arr = [...bar()];
//["x", "a", "b", "y"]

在內(nèi)部,yield*像下面這樣工作的

function* bar() {
    yield "x";
    for (const value of foo()) {
        yield value;
    }
    yield "y";
}

另外,yield*的操作數(shù)不一定非得是一個(gè)generator函數(shù)生成的對(duì)象,可以是任何可迭代的

function* bla() {
    yield "sequence";
    yield* ["of", "yielded"];
    yield "values";
}
const arr = [...bla()];
// ["sequence", "of", "yielded", "values"]

yield*考慮可迭代對(duì)象的最后一個(gè)值

ES6中的很多結(jié)構(gòu)會(huì)忽略generator函數(shù)返回的可迭代對(duì)象的最后一個(gè)值(例如 for of,擴(kuò)展操作符,如上面介紹過(guò)的那樣),但是,yield*的結(jié)果是這個(gè)值

function* genFuncWithReturn() {
    yield "a";
    yield "b";
    return "The result";
}
function* logReturned(genObj) {
    const result = yield* genObj;
    console.log(result); // (A)
}

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

> [...logReturned(genFuncWithReturn())]
The result
[ "a", "b" ]
generators作為數(shù)據(jù)消費(fèi)者(observers)

作為數(shù)據(jù)的消費(fèi)者,generator函數(shù)返回的對(duì)象也實(shí)現(xiàn)了接口Observer

interface Observer {
    next(value? : any) : void;
    return(value? : any) : void;
    throw(error) : void;
}

作為observer,generator暫停執(zhí)行直到它接受到輸入值,這有三種類型的輸入,通過(guò)以下三種observer接口提供的方法

next() 發(fā)送正常的輸入

return() 終止generator

throw() 發(fā)送一個(gè)錯(cuò)誤

通過(guò)next()發(fā)送值

function* dataConsumer() {
    console.log("Started");
    console.log(`1. ${yield}`); // (A)
    console.log(`2. ${yield}`);
    return "result";
}

首先得到generator對(duì)象

const genObj = dataConsumer();

然后執(zhí)行g(shù)enObj.next(),這會(huì)開(kāi)始這個(gè)generator.執(zhí)行到第一個(gè)yield處然后暫停。此時(shí)next()的結(jié)果是yield在行A產(chǎn)出的值(是undifined,因?yàn)檫@地方的yield后面沒(méi)有操作數(shù))

> genObj.next()
//Started
{ value: undefined, done: false }

然后再調(diào)用next()兩次,第一次傳個(gè)參數(shù)"a",第二次傳參數(shù)"b"

> genObj.next("a")
//1. a
{ value: undefined, done: false }

> genObj.next("b")
//2. b
{ value: "result", done: true }

可以看到,第一個(gè)next()調(diào)用的作用僅僅是開(kāi)始這個(gè)generator,只是為了后面的輸入做準(zhǔn)備

可以封裝一下

function coroutine(generatorFunction) {
    return function (...args) {
        const generatorObject = generatorFunction(...args);
        generatorObject.next();
        return generatorObject;
    };
}

使用

const wrapped = coroutine(function* () {
    console.log(`First input: ${yield}`);
    return "DONE";
});

> wrapped().next("hello!")
First input: hello!

return() 和 throw()

generator對(duì)象有兩個(gè)另外的方法,return()和throw(),和next()類似

讓我們回顧一下next()是怎么工作的:

generator暫停在yield操作符

發(fā)送x給這個(gè)yield

繼續(xù)執(zhí)行到下一個(gè)yield,return或者throw:

yield x 導(dǎo)致 next() 返回 {value: x, done: false}

return x 導(dǎo)致 next() 返回 {value:x, done:true}

throw err 導(dǎo)致 next() 拋出err

return()和throw() 和next()類似工作,但在第二步有所不同

return(x) 在 yield的位置執(zhí)行 return x

throw(x) 在yield的位置執(zhí)行throw x

return()終止generator

return() 在 yield的位置執(zhí)行return

function* genFunc1() {
    try {
        console.log("Started");
        yield; // (A)
    } finally {
        console.log("Exiting");
    }
}

> const genObj1 = genFunc1();
> genObj1.next()
Started
{ value: undefined, done: false }
> genObj1.return("Result")
Exiting
{ value: "Result", done: true }

阻止終止

我們可以阻止return()終止generator如果yield是在finally塊內(nèi)(或者在finally中使用return語(yǔ)句)

function* genFunc2() {
    try {
        console.log("Started");
        yield;
    } finally {
        yield "Not done, yet!";
    }
}

這一次,return()沒(méi)有退出generator函數(shù),當(dāng)然,return()返回的對(duì)象的done屬性就是false

> const genObj2 = genFunc2();

> genObj2.next()
Started
{ value: undefined, done: false }

> genObj2.return("Result")
{ value: "Not done, yet!", done: false }

可以再執(zhí)行一次next()

> genObj2.next()
{ value: "Result", done: true }

發(fā)送一個(gè)錯(cuò)誤

throw()在yield的位置拋一個(gè)異常

function* genFunc1() {
    try {
        console.log("Started");
        yield; // (A)
    } catch (error) {
        console.log("Caught: " + error);
    }
}
> const genObj1 = genFunc1();

> genObj1.next()
Started
{ value: undefined, done: false }

> genObj1.throw(new Error("Problem!"))
Caught: Error: Problem!
{ value: undefined, done: true }

yield* 完整的故事

到目前為止,我們只看到以yield的一個(gè)層面: 它傳播生成的值從被調(diào)用者到調(diào)用者。既然我們現(xiàn)在對(duì)generator接受值感興趣,我們就來(lái)看一下yield的另一個(gè)層面:yield*可以發(fā)送調(diào)用者接受的值給被調(diào)用者。在某種程度上,被調(diào)用者變成了活躍的generator,它可以被調(diào)用者生成的對(duì)象控制

function* callee() {
    console.log("callee: " + (yield));
}
function* caller() {
    while (true) {
        yield* callee();
    }
}
> const callerObj = caller();

> callerObj.next() // start
{ value: undefined, done: false }

> callerObj.next("a")
callee: a
{ value: undefined, done: false }

> callerObj.next("b")
callee: b
{ value: undefined, done: false }
generators作為協(xié)同程序(協(xié)作多個(gè)任務(wù))

這一節(jié)介紹generator完整的接口(組合作為數(shù)據(jù)生產(chǎn)者和消費(fèi)者兩種角色)和一個(gè)同時(shí)要使用這兩種角色的使用場(chǎng)景:協(xié)同操作多任務(wù)

完整的接口

interface Generator {
    next(value? : any) : IteratorResult;
    throw(value? : any) : IteratorResult;
    return(value? : any) : IteratorResult;
}
interface IteratorResult {
    value : any;
    done : boolean;
}

接口Generator結(jié)合了我們之前介紹過(guò)的兩個(gè)接口:輸出的Iterator和輸入的Observer

interface Iterator { // data producer
    next() : IteratorResult;
    return?(value? : any) : IteratorResult;
}

interface Observer { // data consumer
    next(value? : any) : void;
    return(value? : any) : void;
    throw(error) : void;
}

合作多任務(wù)

合作多任務(wù)是我們需要generators同時(shí)處理輸入和輸出,在介紹generator是如何工作的之前,讓我們先復(fù)習(xí)一下JavaScript當(dāng)前的并行狀態(tài)

js是單線程的,但有兩種方式可以消除這種限制

多進(jìn)程: Web Worker可以讓我們以多進(jìn)程的方式運(yùn)行js,對(duì)數(shù)據(jù)的共享訪問(wèn)是多進(jìn)程的最大缺陷之一,Web Worker避免這種缺陷通過(guò)不分享任何數(shù)據(jù)。也就是說(shuō),如果你想讓W(xué)eb Worker擁有一段數(shù)據(jù),要么發(fā)送給它一個(gè)數(shù)據(jù)的副本,要么把數(shù)據(jù)傳給它(這樣之后,你就不能再訪問(wèn)這些數(shù)據(jù)了)

合作多任務(wù):有不同的模式和庫(kù)可以嘗試進(jìn)行多任務(wù)處理,運(yùn)行多個(gè)任務(wù),但每次只執(zhí)行一個(gè)任務(wù)。每個(gè)任務(wù)必須顯式地掛起自己,在任務(wù)切換發(fā)生時(shí)給予它完全的控制。在這些嘗試中,數(shù)據(jù)經(jīng)常在任務(wù)之間共享。但由于明確的暫停,幾乎沒(méi)有風(fēng)險(xiǎn)。

通過(guò)generators來(lái)簡(jiǎn)化異步操作

一些基于Promise的庫(kù)通過(guò)generator來(lái)簡(jiǎn)化了異步代碼,generators作為Promise的客戶是非常理想的,因?yàn)樗鼈兛梢詴和V钡浇Y(jié)果返回

下面的例子表明co是如何工作的

co(function* () {
    try {
        const [croftStr, bondStr] = yield Promise.all([  // (A)
            getFile("http://localhost:8000/croft.json"),
            getFile("http://localhost:8000/bond.json"),
        ]);
        const croftJson = JSON.parse(croftStr);
        const bondJson = JSON.parse(bondStr);

        console.log(croftJson);
        console.log(bondJson);
    } catch (e) {
        console.log("Failure to read: " + e);
    }
});

注意這段代碼看起來(lái)是多么的同步啊,雖然它在行A處執(zhí)行了一個(gè)異步調(diào)用。

使用generators對(duì)co的一個(gè)簡(jiǎn)單的實(shí)現(xiàn)

function co(genFunc) {
    const genObj = genFunc();
    step(genObj.next());

    function step({value,done}) {
        if (!done) {
            // A Promise was yielded
            value
            .then(result => {
                step(genObj.next(result)); // (A)
            })
            .catch(error => {
                step(genObj.throw(error)); // (B)
            });
        }
    }
}

這里忽略了next()(行A)和throw()(行B)可以回拋異常

借助上面的使用分析一下:

首先得到generator對(duì)象

const genObj = genFunc();

然后將genObj.next()的返回值傳遞給step方法

step()中獲取到value和done,如果generator沒(méi)有執(zhí)行完,當(dāng)前的value就是上面使用中定義的promise

等到promise執(zhí)行完,然后將結(jié)果result傳遞給generator函數(shù)

genObj.next(result)

然后在generator中程序繼續(xù)往下執(zhí)行

const [croftStr, bondStr] = yield XXXX
.
.
.
.

注意行A處遞歸調(diào)用step(genObj.next(result)),使得generator函數(shù)中可以存在多個(gè)異步調(diào)用,而co都能處理

整個(gè)過(guò)程多么的巧妙啊。。。。。。。。。

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

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

相關(guān)文章

  • ES6中的異步編程:Generators函數(shù)(一)

    摘要:由于可以使用語(yǔ)句來(lái)暫停異步操作,這讓異步編程的代碼,很像同步數(shù)據(jù)流方法一樣。該臨時(shí)函數(shù)就叫做函數(shù)。下面就是簡(jiǎn)單的函數(shù)轉(zhuǎn)換器。 訪問(wèn)原文地址 對(duì)ES6的generators的介紹分為3個(gè)部分 第一部分base介紹及使用 第二部分基于generators和Promise實(shí)現(xiàn)最強(qiáng)大的異步處理邏輯 概述 Generator函數(shù)是協(xié)程在ES6的實(shí)現(xiàn),用來(lái)做異步流程的封裝,最大特點(diǎn)就是可以交出...

    ztyzz 評(píng)論0 收藏0
  • JavaScript 異步編程的四種方式

    摘要:異步編程是每個(gè)使用編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的請(qǐng)求,或是的各種異步。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。利用一種鏈?zhǔn)秸{(diào)用的方法來(lái)組織異步代碼,可以將原來(lái)以回調(diào)函數(shù)形式調(diào)用的代碼改為鏈?zhǔn)秸{(diào)用。 異步編程是每個(gè)使用 JavaScript 編程的人都會(huì)遇到的問(wèn)題,無(wú)論是前端的 ajax 請(qǐng)求,或是 node 的各種異步 API。本文就來(lái)總結(jié)一下常見(jiàn)的四種處理異步編程的方法。...

    microelec 評(píng)論0 收藏0
  • 深入理解 Generator 函數(shù)

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

    jzzlee 評(píng)論0 收藏0
  • ES6中的異步編程:Generators函數(shù)+Promise:最強(qiáng)大的異步處理方式

    摘要:更好的異步編程上面的方法可以適用于那些比較簡(jiǎn)單的異步工作流程。小結(jié)的組合目前是最強(qiáng)大,也是最優(yōu)雅的異步流程管理編程方式。 訪問(wèn)原文地址 generators主要作用就是提供了一種,單線程的,很像同步方法的編程風(fēng)格,方便你把異步實(shí)現(xiàn)的那些細(xì)節(jié)藏在別處。這讓我們可以用一種很自然的方式書(shū)寫(xiě)我們代碼中的流程和狀態(tài)邏輯,不再需要去遵循那些奇怪的異步編程風(fēng)格。 換句話說(shuō),通過(guò)將我們generato...

    Taonce 評(píng)論0 收藏0
  • ES6新特性 iterators and Generators

    摘要:在函數(shù)定義上使用關(guān)鍵字來(lái)表示方法調(diào)用時(shí)返回的值。是一個(gè)有屬性的。這個(gè)指向一個(gè)函數(shù),這個(gè)函數(shù)返回關(guān)于這個(gè)對(duì)象的。在中所有的集合類對(duì)象和字符串都是,并且有自己默認(rèn)的。注意本身是不返回任何值的,它只向外部產(chǎn)生值。 ES6新特性 iterators and Generators ES6中引入了許多新特性,目前大量的JavaScript項(xiàng)目已經(jīng)使用了ES6來(lái)進(jìn)行開(kāi)發(fā),那么熟悉這些新的特性是十分必...

    pf_miles 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

zhaot

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<