function*

generators

Description

Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances.

Generators in JavaScript -- especially when combined with Promises -- are a very powerful tool for asynchronous programming as they mitigate -- if not entirely eliminate -- the problems with callbacks, such as Callback Hell and Inversion of Control. However, an even simpler solution to these problems can be achieved with async functions.

Declaration

The function* declaration (function keyword followed by an asterisk) defines a generator function, which returns a Generator object.

function* generator(i) {
  yield i;
  yield i + 10;
}

const gen = generator(10);

console.log(gen.next().value);
// expected output: 10

console.log(gen.next().value);
// expected output: 20

Methods

Also inherits methods from: Iterator

 

Generator.prototype.next()

Returns a value yielded by the yield expression.

 

Generator.prototype.return()

Returns the given value and finishes the generator.

 

Generator.prototype.throw()

Throws an error to a generator (also finishes the generator, unless caught from within that generator).

function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

const generator = generateSequence();

for(let value of generator) {
  console.log(value); // 1, then 2
}

...

function* generateSequence() {
  yield 1;
  yield 2;
  yield 3;
}

const sequence = [0, ...generateSequence()];

console.log(sequence); // 0, 1, 2, 3

Iterator

const range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    return this;
  },
  next() {
    if (this.current === undefined) {
      this.current = this.from;
    }

    if (this.current <= this.to) {
      return {
        done: false,
        value: this.current++
      };
    } else {
      delete this.current;
      return {
        done: true
      };
    }
  }
};

for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

console.log(Math.max(...range)); // 5

Generator

const range = {
  from: 1,
  to: 5,

  *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*()
    for(let value = this.from; value <= this.to; value++) {
      yield value;
    }
  }
};

console.log( [...range] ); // 1,2,3,4,5
// генератор для получения и показа аватара, он yield'ит промисы
function* showUserAvatar() {

  let userFetch = yield fetch('/article/generator/user.json');
  let userInfo = yield userFetch.json();

  let githubFetch = yield fetch(`https://api.github.com/users/${userInfo.name}`);
  let githubUserInfo = yield githubFetch.json();

  let img = new Image();
  img.src = githubUserInfo.avatar_url;
  img.className = "promise-avatar-example";
  document.body.appendChild(img);

  yield new Promise(resolve => setTimeout(resolve, 3000));

  img.remove();

  return img.src;
}

// вспомогательная функция-чернорабочий для выполнения промисов из generator
function execute(generator, yieldValue) {

  let next = generator.next(yieldValue);

  if (!next.done) {
    next.value.then(
      result => execute(generator, result),
      err => generator.throw(err)
    );
  } else {
    // обработаем результат return из генератора обычно здесь вызов callback или что-то в этом духе
    console.log(next.value);
  }

}

execute( showUserAvatar() );

Browser compatibility

Generators

By Andrew Bogomolov