Как написать генератор в классе JavaScript?

Обычно я пишу код следующим образом:

//definition
exports.getReply = function * (msg){
    //...
    return reply; 
}
//usage
var msg = yield getReply ('hello');

но как я могу писать и использовать генератор в классе es6 и из него? Я пробовал это:

class Reply{
    *getReply (msg){
        //...
        return reply; 
    }
     *otherFun(){
        this.getReply();  //`this` seem to have no access to `getReply`
    }
}
var Reply = new Reply();
Reply.getReply();   //out of class,how can I get access to `getReply`?

Я также пробовал:

 class Reply{
      getReply(){
         return function*(msg){
            //...
            return reply;
         }
      }
    }

Все эти два метода кажутся неправильными ответами. Итак, как я могу правильно написать функции генератора в классе?

Ответ 1

Изменение: Добавить больше примеров.
Ваше определение class является (почти) правильным. Ошибка была в экземпляре var Reply = new Reply(); , Это пытается переопределить переменную, присвоенную имени класса. Также ожидается, что функция generator что-то yield. Я разработал небольшой OP-код, чтобы показать рабочий пример.

class Reply {
  //added for test purpose
  constructor(...args) {
      this.args = args;
    }
    * getReply(msg) {
      for (let arg in this.args) {
        let reply = msg + this.args[arg];
        //generator should yield something
        yield reply;
      }
      //next call returns (yields) {done:true,value:undefined}
    }
    * otherFun() {
      yield this.getReply('Nice to meet you '); //yields Generator object
      yield this.getReply('See you '); //Yes, this can access 
      //next call yields {done:true, value:undefined}
    }
    * evenMore() {
      yield* this.getReply('I miss you '); //yields generator result(s)
      yield* this.getReply('I miss you even more ');
    }
}
//now test what we have
const reply = new Reply('Peter', 'James', 'John');
//let and var here are interchangeable because of Global scope
var r = reply.getReply('Hello ');
var msg = r.next(); //{done:false,value:"..."}
while (!msg.done) {
  console.log(msg.value);
  msg = r.next();
}
var other = reply.otherFun();
var o = other.next(); //{done:false,value:Generator}
while (!o.done) {
  let gen = o.value;
  msg = gen.next();
  while (!msg.done) {
    console.log(msg.value);
    msg = gen.next();
  }
  o = other.next();
}
var more = reply.evenMore();
msg = more.next();
while (!msg.done) {
  console.log(msg.value);
  msg = more.next();
}
//update of 1/12/2019
//more examples
for (let r of reply.getReply('for ')) {
  console.log(r);
}
for (let r of reply.evenMore()) {
  console.log(r);
}
//note that the following doesn't work because of lack of star (*) inside the generator function
for (let r of reply.otherFun()) {
  console.log(r);
}

Ответ 2

Генераторы - это функции с .next() для получения значений yield 'ed, или вы можете yield функцию генератора, чтобы он знал, что не нужно "ждать" при встрече с оператором yield для a .next для вызова (reply.getReply().next(fn))

Ваша вторая часть кода почти правильная:

class Reply{
    *getReply (msg){
        //...
        return reply; 
    }
     *otherFun(){
        this.getReply();  //`this` seem to have no access to `getReply`
    }
}
var Reply = new Reply();
Reply.getReply();   //out of class,how can I get access to `getReply`?

Прежде всего, используйте const или let при работе в ES6 и используйте только верхний регистр для классов. Вы пытаетесь перезаписать оператор class Reply оператором var Reply =, что невозможно, так как Identifier 'Reply' уже объявлен.

Ответ, который вы ищете, следующий:
Как и в первом примере, вы должны yield выполнять функции генератора, поэтому ваш способ должен выглядеть следующим образом:

class Reply{
    *getReply (msg){
        // yield something here, otherwise you should use a normal function
        return reply;
    }
     *otherFun(){
        const reply = yield this.getReply();  // yield the generator so it does what it needs to and doesn't wait for the .next() to be called on it
        return `${reply} within class ${this.constructor.name}`;
    }
}
const reply = new Reply();
const answer = yield reply.getReply('foo'); 
// getReply is a generator function, so it needs a `yield` or `.next()` to run beyond the first `yield` in the function