почему `throw` Метод интерфейса итератора никогда не вызывается в цикле for-of?

#javascript #iterator #iterable #ecmascript-next

#javascript #итератор #итерируемый #ecmascript-следующий

Вопрос:

согласно спецификации ECMAScript объект, реализующий интерфейс итератора, должен иметь next метод, но есть также необязательные методы return и throw (смотрите Спецификации здесь:https://tc39.es/ecma262/#sec-iteration)

приводя пример кода во фрагменте, почему throw метод никогда не вызывается в цикле for-of, но вместо него вызывается return метод?

 class Iterable {
  constructor(name) {
    this.name = name;
  }
  [Symbol.iterator]() {
    let done = false;

    function implementation() {
      if (done) {
        return {
          done: true,
          value: undefined,
        };
      }
      done = true;
      return {
        done: false,
        value: undefined,
      };
    }

    return {
      next: (...args) => {
        console.log(
          `${this.name}: next called, done=${JSON.stringify(
            done
          )}, args=${JSON.stringify(args)}`
        );
        return implementation();
      },
      return: (...args) => {
        console.log(
          `${this.name}: return called, done=${JSON.stringify(
            done
          )}, args=${JSON.stringify(args)}`
        );
        return implementation();
      },
      throw: (...args) => {
        console.log(
          `${this.name}: throw called, done=${JSON.stringify(
            done
          )}, args=${JSON.stringify(args)}`
        );
        return implementation();
      },
    };
  }
}

(() => {
  for (const item of new Iterable("standard")) {
    // pass
  }
})();

(() => {
  for (const item of new Iterable("return")) {
    return "test";
  }
})();

(() => {
  for (const item of new Iterable("break")) {
    break;
  }
})();

(() => {
  for (const item of new Iterable("continue")) {
    continue;
  }
})();

(() => {
  try {
    for (const item of new Iterable("throw")) {
      throw new Error(`${item}`);
    }
  } catch {
    // pass
  }
})();  

вывод на консоль:

 standard: next called, done=false, args=[]
standard: next called, done=true, args=[]
return: next called, done=false, args=[]
return: return called, done=true, args=[]
break: next called, done=false, args=[]
break: return called, done=true, args=[]
continue: next called, done=false, args=[]
continue: next called, done=true, args=[]
throw: next called, done=false, args=[]
throw: return called, done=true, args=[]
  

Комментарии:

1. Почему должен быть вызван throw метод? Это было бы катастрофой, потому что это делает код непредсказуемым. throw используется для информирования генератора об ошибке.

2. @abetteroliver имеет смысл, не могли бы вы привести мне пример, как это используется? или что скрывается за вашим последним предложением — я имею в виду, предназначено ли оно для использования при ручном использовании генератора?