Dieser Artikel bietet Ihnen eine Einführung in die Verwendung der Generatorfunktion in ES6. Ich hoffe, dass er für Freunde hilfreich ist.
1. Was ist die Generatorfunktion
Die Generatorfunktion ist eine im ES6-Standard vorgeschlagene asynchrone Programmierlösung. Der größte Unterschied zwischen dieser Art von Funktion und gewöhnlichen Funktionen besteht darin, dass sie die Ausführung anhalten und an der angehaltenen Position wieder fortsetzen kann.
Syntaktisch gesehen ist die Generatorfunktion eine Zustandsmaschine, die viele interne Zustände kapselt.
Im Wesentlichen ist die Generatorfunktion ein Traverser-Objektgenerator. (Für das Traverser-Objekt können Sie auf diesen Artikel von Lehrer Ruan Yifeng verweisen.) Die Generator-Funktion gibt ein Traverser-Objekt zurück, indem Sie nacheinander jeden Zustand innerhalb der Funktion erhalten.
2. Grundlegende Syntax
1. Generatorfunktion definieren
Der Unterschied zwischen der Definition einer Generatorfunktion und der Definition einer gewöhnlichen Funktion ist:
Zwischen dem Funktionsschlüsselwort und dem Funktionsnamen steht ein * (Sternchen).
Yield wird innerhalb der Funktion verwendet, um den internen Zustand jeder Funktion zu definieren.
Wenn innerhalb der Funktion eine Return-Anweisung vorhanden ist, handelt es sich um den letzten Zustand innerhalb der Funktion.
Schauen wir uns ein einfaches Beispiel an:
// 定义 function* sayHello() { yield 'hello'; yield 'world'; return 'ending'; } // 调用 // 注意,hw获取到的值是一个遍历器对象 let g = sayHello();
Das obige Beispiel definiert eine Generatorfunktion namens sayHello
, die zwei yield
-Ausdrücke und einen return
-Ausdruck hat. Daher gibt es innerhalb der Funktion drei Zustände: hello
, world
und return
-Anweisung (Ausführung beenden). Rufen Sie abschließend diese Funktion auf, um ein Traverser-Objekt abzurufen und es der Variablen g
zuzuweisen.
Die Aufrufmethode der Generatorfunktion ist genau die gleiche wie die der gewöhnlichen Funktion, 函数名()
. Der Unterschied ist:
Die Generatorfunktion wird nicht sofort nach dem Aufruf ausgeführt. Wie können wir sie also mit der Ausführung des internen Codes beginnen lassen? Wie bekomme ich jeden Staat hinein? An diesem Punkt müssen wir die Methode .next() des zurückgegebenen Generatorobjekts aufrufen, um die Ausführung des Codes zu starten und den Zeiger in den nächsten Zustand zu bewegen.
Nehmen Sie das obige Beispiel als Beispiel:
g.next(); // { value: 'hello', done: false } g.next(); // { value: 'world', done: false } g.next(); // { value: 'ending', done: true } g.next(); // { value: undefined, done: true }
Im obigen Code werden insgesamt vier g
.next()
Methoden dieses Traverser-Objekts aufgerufen. Beim ersten Aufruf beginnt die sayHello
-Generatorfunktion mit der Ausführung und die Ausführung wird angehalten, bis sie auf den ersten yield
-Ausdruck trifft. Die .next()
-Methode gibt ein Objekt zurück, ihr value
-Attribut ist der Wert des aktuellen yield
-Ausdrucks hello
und der Wert des done
-Attributs false
gibt an, dass die Durchquerung noch nicht beendet ist. Wenn
.next()
zum zweiten Mal aufruft, wird der zweite yield
-Ausdruck ausgeführt, die Ausführung wird angehalten und das entsprechende Objekt wird zurückgegeben.
ruft .next()
zum dritten Mal auf und die Funktion wird bis zur letzten return
-Anweisung ausgeführt, die das Ende der Durchquerung des Traverser-Objekts g
markiert, also den value
-Attributwert in Das zurückgegebene Objekt ist gefolgte Wert ist return
und der Attributwert ending
ist done
, was angibt, dass die Durchquerung beendet ist. true
zurück. {value: undefined, done: true }
-Methode in den nächsten internen Zustand, sodass dies tatsächlich eine Möglichkeit bietet, dies zu tun pausiert die Ausführung, der .next()
-Ausdruck ist das Pausenflag. Die Operationslogik der yield
-Methode des
-Traverser-Objekts lautet wie folgt. Wenn .next()
yield
folgt, als yield
-Attributwert des zurückgegebenen Objekts verwendet. value
.next()
-Ausdruck gefunden wird. yield
yield
-Anweisung ausgeführt und der Wert des Ausdrucks nach der return
-Anweisung wird als Rückgabewert verwendet Objekt Der return
-Attributwert. value
return
-Attributwert des zurückgegebenen Objekts value
. undefined
Es ist erwähnenswert:
yield
关键字只能出现在Generator函数中,出现在别的函数中会报错。// 出现在普通函数中,报错 (function () { yield 'hello'; })() // forEach不是Generator函数,报错 [1, 2, 3, 4, 5].forEach(val => { yield val });
yield
关键字后面跟的表达式,是惰性求值的。 只有当调用.next()
方法、内部状态暂停到当前yield
时,才会计算其后面跟的表达式的值。这等于为JavaScript提供了手动的“惰性求值”的语法功能。function* step() { yield 'step1'; // 下面的yield后面的表达式不会立即求值, // 只有暂停到这一行时,才会计算表达式的值。 yield 'step' + 2; yield 'setp3'; return 'end'; }
yield
表达式本身是没有返回值的,或者说它的返回值为undefined
。使用.next()传参可以为其设置返回值。(后面会讲到)function* gen() { for (let i = 0; i < 5; i++) { let res = yield; // yield表达式本身没有返回值 console.log(res); // undefined } } let g = gen(); g.next(); // {value: 0, done: false} g.next(); // {value: 1, done: false} g.next(); // {value: 2, done: false}
yield与return的异同:
相同点:
不同点:
前面我们说到过,yield
表达式自身没有返回值,或者说返回值永远是undefined
。但是,我们可以通过给.next()
方法传入一个参数,来设置上一个(是上一个)yield
表达式返回值。
来看一个例子:
function* conoleNum() { console.log('Started'); console.log(`data: ${yield}`); console.log(`data: ${yield}`); return 'Ending'; } let g = conoleNum(); g.next(); // 控制台输出:'Started' g.next('a'); // 控制台输出:'data: a' // 不传入参数'a',就会输出'data: undefined' g.next('b'); // 控制台输出:'data: b' // 不传入参数'a',就会输出'data: undefined'
上面的例子,需要强调一个不易理解的地方。
第一次调用.next()
,此时函数暂停在代码第三行的yield
表达式处。记得吗?yield
会暂停函数执行,此时打印它的console.log()
,也就是代码第三行的console,由于暂停并没有被执行,所以不会打印出结果,只输出了代码第二行的'Started'。
当第二次调用.next()
方法时,传入参数'a'
,函数暂停在代码第四行的yield
语句处。此时参数'a'
会被当做上一个yield
表达式的返回值,也就是代码第三行的yiled
表达式的返回值,所以此时控制台输出'data: a'
。而代码第四行的console.log()
由于暂停,没有被输出。
第三次调用,同理。所以输出'data: b'
。
Generator函数返回的遍历器对象,都有一个.throw()
方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。
function* gen() { try { yield; } catch (e) { console.log('内部捕获', e); } }; var g = gen(); // 下面执行一次.next() // 是为了让gen函数体执行进入try语句中的yield处 // 这样抛出错误,gen函数内部的catch语句才能捕获错误 g.next(); try { g.throw('a'); g.throw('b'); } catch (e) { console.log('外部捕获', e); }
上面例子中,遍历器对象g
在gen
函数体外连续抛出两个错误。第一个错误被gen
函数体内的catch
语句捕获。g
第二次抛出错误,由于gen
函数内部的catch
语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就会被抛出gen
函数体,被函数体外的catch
语句捕获。
值得注意的是:
try...catch
代码块,那么遍历器对象的throw
方法抛出的错误,将被外部try...catch
代码块捕获。try...catch
代码块,那么程序将报错,直接中断执行。遍历器对象的throw
方法被捕获以后,会附带执行一次.next()
方法,代码执行会暂停到下一条yield
表达式处。看下面这个例子:
function* gen(){ try { yield console.log('a'); } catch (e) { console.log(e); // 'Error' } yield console.log('b'); yield console.log('c'); } var g = gen(); g.next(); // 控制台输出:'a' g.throw('Error'); // 控制台输出:'b' // throw的错误被内部catch语句捕获, // 会自动在执行一次g.next() g.next(); // 控制台输出:'c'
Generator函数返回的遍历器对象,还有一个.return()
方法,可以返回给定的值,并且直接结束对遍历器对象的遍历。
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next(); // { value: 1, done: false } // 提前结束对g的遍历。尽管yield还没有执行完 // 此时done属性值为true,说明遍历结束 g.return('foo'); // { value: "foo", done: true } g.next(); // { value: undefined, done: true }
如果.return()
方法调用时,不提供参数,则返回值的value
属性为undefined
。
yield*
用来在一个Generator函数里面执行另一个Generator函数。
如果在一个Generator函数内部,直接调用另一个Generator函数,默认情况下是没有效果的。
function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 直接调用gen1() gen1(); yield 'y'; } // 遍历器对象可以使用for...of遍历所有状态 for (let v of gen2()){ 只输出了gen1的状态 console.log(v); // 'x' 'y' }
上面的例子中,gen1
和gen2
都是Generator函数,在gen2
里面直接调用gen1
,是不会有效果的。
这个就需要用到 yield*
表达式。
function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 用 yield* 调用gen1() yield* gen1(); yield 'y'; } for (let v of gen2()){ 输出了gen1、gen2的状态 console.log(v); // 'x' 'a' 'b' 'y' }
小节
In diesem Artikel werden hauptsächlich die grundlegende Syntax und einige Details der Generatorfunktion, die Definition der Generatorfunktion, der Ertragsausdruck, die .next()-Methode und Parameterübergabe, die .throw()-Methode, die .return()-Methode und der Ertrag erläutert * Ausdruck.
Wie am Anfang des Artikels erwähnt, ist die Generatorfunktion eine von ES6 vorgeschlagene Lösung für die asynchrone Programmierung. In praktischen Anwendungen folgt auf das Schlüsselwort yield im Allgemeinen eine asynchrone Operation. Wenn die asynchrone Operation erfolgreich zurückgegeben wird, wird die Methode .next() aufgerufen, um den asynchronen Prozess an den nächsten Yield-Ausdruck zu übergeben.
Dieser Artikel ist hier zu Ende. Weitere spannende Inhalte finden Sie in der Spalte JavaScript-Video-Tutorial auf der chinesischen PHP-Website!
Das obige ist der detaillierte Inhalt vonEinführung in die Verwendung der Generatorfunktion in ES6. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!