Heim  >  Artikel  >  Web-Frontend  >  Einführung in die Verwendung der Generatorfunktion in ES6

Einführung in die Verwendung der Generatorfunktion in ES6

不言
不言nach vorne
2019-03-30 09:54:262109Durchsuche

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:

  • Nach dem Funktionsaufruf wird der interne Code (ab der ersten Zeile) nicht sofort ausgeführt.
  • Nach dem Aufruf der Funktion gibt es einen Rückgabewert. Dieser Wert ist ein Zeigerobjekt, das auf den internen Status zeigt. Es handelt sich im Wesentlichen um ein Traverserobjekt, das den internen Status der Funktion enthält.

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

Der vierte und weitere Aufrufe der .next()-Methode geben

zurück. {value: undefined, done: true }

2. Ertragsausdruck

Das von der Generatorfunktion zurückgegebene Traverser-Objekt wechselt nur durch Aufrufen der

-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()

    auf einen
  1. -Ausdruck trifft, wird die Ausführung nachfolgender Vorgänge angehalten und der Wert des Ausdrucks, der unmittelbar auf yield folgt, als yield-Attributwert des zurückgegebenen Objekts verwendet. value
  2. Wenn die
  3. -Methode das nächste Mal aufgerufen wird, wird die Ausführung fortgesetzt, bis der nächste .next()-Ausdruck gefunden wird. yield
  4. Wenn kein neuer
  5. -Ausdruck gefunden wird, wird die Funktion bis zum Ende der Funktion bis zur yield-Anweisung ausgeführt und der Wert des Ausdrucks nach der return-Anweisung wird als Rückgabewert verwendet Objekt Der return-Attributwert. value
  6. Wenn die Funktion keine
  7. -Anweisung hat, ist der return-Attributwert des zurückgegebenen Objekts value. undefined

Es ist erwähnenswert:

  1. yield关键字只能出现在Generator函数中,出现在别的函数中会报错。
  // 出现在普通函数中,报错
  (function () {
    yield 'hello';
  })()

  // forEach不是Generator函数,报错
  [1, 2, 3, 4, 5].forEach(val => {
    yield val
  });
  1. yield关键字后面跟的表达式,是惰性求值的。 只有当调用.next()方法、内部状态暂停到当前yield时,才会计算其后面跟的表达式的值。这等于为JavaScript提供了手动的“惰性求值”的语法功能。
function* step() {
  yield 'step1';

  // 下面的yield后面的表达式不会立即求值,
  // 只有暂停到这一行时,才会计算表达式的值。
  yield 'step' + 2;

  yield 'setp3';
  return 'end';
}
  1. 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表达式只是暂停函数向后执行,return是直接结束函数执行。
  • yield表达式可以出现多次,后面还可以有代码。return只能出现一次,后面的代码不会执行,在一些情况下还会报错。
  • 正常函数只能返回一个值,因为只能执行一次return。Generator函数可以返回一系列的值,因为可以有任意多个yield。

3、.next()方法传参

前面我们说到过,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'

4、Generator.prototype.throw()

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);
}

上面例子中,遍历器对象ggen函数体外连续抛出两个错误。第一个错误被gen函数体内的catch语句捕获。g第二次抛出错误,由于gen函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就会被抛出gen函数体,被函数体外的catch语句捕获。

值得注意的是:

  • 如果Generator函数内部没有部署try...catch代码块,那么遍历器对象的throw方法抛出的错误,将被外部try...catch代码块捕获。
  • 如果Generator函数内部和外部都没有部署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'

5、Generator.prototype.return()

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

6、yield* 表达式

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'
}

上面的例子中,gen1gen2都是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!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen