Maison > interface Web > js tutoriel > Introduction à l'utilisation de la fonction Générateur dans ES6

Introduction à l'utilisation de la fonction Générateur dans ES6

不言
Libérer: 2019-03-30 09:54:26
avant
2215 Les gens l'ont consulté

Cet article vous présente une introduction à l'utilisation de la fonction Générateur dans ES6. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.

1. Qu'est-ce que la fonction Générateur

La fonction Générateur est une solution de programmation asynchrone proposée dans le standard ES6. La plus grande différence entre ce type de fonction et les fonctions ordinaires est qu'elle peut suspendre l'exécution et reprendre l'exécution à partir de la position suspendue.

Syntaxiquement parlant, la fonction Générateur est une machine à états qui encapsule de nombreux états internes.

Essentiellement, la fonction Générateur est un générateur d'objets traverseurs. (Pour l'objet traverseur, vous pouvez vous référer à cet article du professeur Ruan Yifeng) La fonction Générateur renvoie un objet traverseur, vous pouvez obtenir tour à tour chaque état à l'intérieur de la fonction.

2. Syntaxe de base

1. Définir la fonction Générateur

La différence entre définir une fonction Générateur et définir une fonction ordinaire est :

Il y a un * (astérisque) entre le mot-clé de la fonction et le nom de la fonction.
Le rendement est utilisé à l'intérieur de la fonction pour définir l'état interne de chaque fonction.
S'il y a une instruction return à l'intérieur de la fonction, alors c'est le dernier état à l'intérieur de la fonction.

Regardons un exemple simple :

// 定义
function* sayHello() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
// 调用
// 注意,hw获取到的值是一个遍历器对象
let g = sayHello();
Copier après la connexion

L'exemple ci-dessus définit une fonction Générateur nommée sayHello, qui contient deux expressions yield dans sa formule et un return expression. Par conséquent, il y a trois états à l'intérieur de la fonction : les instructions hello, world et return (fin d'exécution). Enfin, appelez cette fonction pour obtenir un objet traverseur et affectez-le à la variable g.

La méthode d'appel de la fonction Générateur est exactement la même que la fonction ordinaire, 函数名(). La différence est :

  • Après l'appel de la fonction, le code interne (à partir de la première ligne) ne sera pas exécuté immédiatement.
  • Il y aura une valeur de retour après l'appel de la fonction. Cette valeur est un objet pointeur pointant vers l'état interne. Il s'agit essentiellement d'un objet traverseur contenant l'état interne de la fonction.

La fonction Générateur ne sera pas exécutée immédiatement après avoir été appelée. Alors, comment la laisser commencer à exécuter le code interne ? Comment faire entrer chaque État à l’intérieur ? À ce stade, nous devons appeler la méthode .next() de l'objet générateur renvoyé pour démarrer l'exécution du code et déplacer le pointeur vers l'état suivant.

Prenons l'exemple ci-dessus comme exemple :

g.next();
// { value: 'hello', done: false }
g.next();
// { value: 'world', done: false }
g.next();
// { value: 'ending', done: true }
g.next();
// { value: undefined, done: true }
Copier après la connexion

Dans le code ci-dessus, un total de quatre méthodes g.next() de cet objet traverseur sont appelées. La première fois qu'elle est appelée, la fonction sayHello Generator commence à s'exécuter et l'exécution sera suspendue jusqu'à ce qu'elle rencontre la première yield expression. La méthode .next() renverra un objet, son attribut value est la valeur de l'expression yield actuelle hello et la valeur de l'attribut done false indique que le parcours n'est pas encore terminé. La deuxième fois que

appelle .next(), il s'exécutera sur la deuxième expression yield, mettra l'exécution en pause et renverra l'objet correspondant.

appelle .next() pour la troisième fois, et la fonction s'exécute jusqu'à la dernière instruction return, qui marque la fin du parcours de l'objet traverseur g, donc la valeur de l'attribut value dans l'objet renvoyé est est return et la valeur de l'attribut ending est done, indiquant que le parcours est terminé. true

Le quatrième appel et les suivants à la méthode .next() renverront

. {value: undefined, done: true }

2. Expression de rendement

L'objet traverseur renvoyé par la fonction Générateur passera à l'état interne suivant uniquement en appelant la méthode

, cela fournit donc en fait un moyen d'accéder à la fonction qui met l'exécution en pause, l'expression .next() est l'indicateur de pause. La logique de fonctionnement de la méthode yield de l'objet traverseur

est la suivante. Lorsque .next()

    rencontre une expression
  1. , il suspend l'exécution des opérations suivantes et utilise la valeur de l'expression qui suit immédiatement yield comme valeur d'attribut yield de l'objet renvoyé. value
  2. La prochaine fois que la méthode
  3. est appelée, l'exécution continue jusqu'à ce que la prochaine expression .next() soit rencontrée. yield
  4. Si aucune nouvelle expression
  5. n'est rencontrée, elle s'exécutera jusqu'à la fin de la fonction jusqu'à l'instruction yield, et la valeur de l'expression après l'instruction return sera utilisée comme valeur renvoyée object La valeur de l'attribut return. value
  6. Si la fonction n'a pas d'instruction
  7. , la valeur de l'attribut return de l'objet renvoyé est value. undefined

Il est à noter :

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

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

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

  yield 'setp3';
  return 'end';
}
Copier après la connexion
  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}
Copier après la connexion

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'
Copier après la connexion

上面的例子,需要强调一个不易理解的地方。

第一次调用.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);
}
Copier après la connexion

上面例子中,遍历器对象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'
Copier après la connexion

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 }
Copier après la connexion

如果.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'
}
Copier après la connexion

上面的例子中,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'
}
Copier après la connexion

小节

Cet article explique principalement la syntaxe de base et quelques détails de la fonction Generator, la définition de la fonction Generator, l'expression de rendement, la méthode .next() et le passage des paramètres, la méthode .throw(), la méthode .return() et le rendement * expression.

Comme mentionné en début d'article, la fonction Générateur est une solution de programmation asynchrone proposée par ES6. Dans les applications pratiques, le mot-clé rendement est généralement suivi d'une opération asynchrone. Lorsque l'opération asynchrone est renvoyée avec succès, la méthode .next() est appelée pour transmettre le processus asynchrone à l'expression rendement suivante.

Cet article est terminé ici. Pour un contenu plus passionnant, vous pouvez faire attention à la colonne Tutoriel vidéo JavaScript du site Web PHP chinois !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal