Generators

Samuel Silva

What is a generator?

When standard Javascript functions are invoked, they run until they return a value or reach the end of the function. At that point, they’re finished. ES6 introduces a new type of function called generators.

A generator is a special type of function that can be entered and exited a number of times. You might hear people describe it as, “a function that can be paused”.

Functions vs. Generator Functions

function test() {
    console.log('This')
    console.log('is')
    console.log('a')
    console.log('test')
}

test(); // This is a test

function* testGenerator() {
    console.log('This')
    console.log('is')
    console.log('a')
    console.log('generator')
    console.log('test')
}

testGenerator(); // Nothing here

Invoking Generators

function* testGenerator() {
    console.log('This')
    console.log('is')
    console.log('a')
    console.log('generator')
    console.log('test')
}

const generator = testGenerator(); // Return a Generators instance

generator.next(); // Remember Iterators?

This executes the entire generator function by using a single .next() method because we haven’t established a point for where to pause / exit our function. We’ll do this below by introducing the yield keyword.

Pausing a generator

function* testGenerator() {
    console.log('This')
    console.log('is')

    yield

    console.log('a')
    console.log('generator')
    console.log('test')
}

const generator = testGenerator(); // Return the generator instance

generator.next();

// This
// is


Passing data to a generator

function* testGenerator() {
 console.log('This')
  console.log('is')

  yield 10
  yield 20
  yield 30
  
  console.log('a')
  console.log('generator')
  console.log('test')
}

const generator = testGenerator(); // Nothing here

// This is

console.log(generator.next()); // {value: 10, done: false}
console.log(generator.next()); // {value: 20, done: false}
console.log(generator.next()); // {value: 30, done: false}

// a
// generator
// test

console.log(generator.next()); // ?
console.log(generator.next()); // ? 


This iterator object tells us two things:

 

- value: The data that was retrieved from within the generator function at the time when we yielded / exited.

 

- done: whether the generator function has finished it’s execution. In this example it has not because we would need to re-enter the generator with an additional .next() iterator call to continue, as we’ll see below.

Async Functions

const dataBase = {
    id: 1,
    name: 'Samuel',
    lastName: 'Castro'
}
  
const findUser = () => {
  console.log('Finding user');
    
  return new Promise((resolve) => {
     setTimeout(() => resolve(dataBase), 3000);
  });
}
 
// Generator
const initUserGenerator = function*() {
  const result = yield findUser()
  return `User: ${result.name} ${result.lastName}`
}

const iterator = initUserGenerator(); // start out generator
const iteration = iterator.next(); // {value: Promise, done: false}


iteration.value.then(user => {
  console.log(user); // {id: 1, name: "Samuel", lastName: "Castro"}
  
  const nextIteration = iterator.next(user)
  
  console.log(nextIteration); // {value: "User: Samuel Castro", done: true}
}) 



A more generic Async Generator

const dataBase = {
    id: 1,
    name: 'Samuel',
    lastName: 'Castro'
}
  
const findUser = () => {
    console.log('Finding user');
    
    return new Promise((resolve) => {
        setTimeout(() => resolve(dataBase), 3000);
    });
}
 
function async(makeGenerator){
  return function () {
    var generator = makeGenerator.apply(this, arguments);

    function handle(result){
      // result => { done: [Boolean], value: [Object] }
      if (result.done) return Promise.resolve(result.value);

      return Promise.resolve(result.value).then(function (res){
        return handle(generator.next(res));
      }, function (err){
        return handle(generator.throw(err));
      });
    }

    try {
      return handle(generator.next());
    } catch (ex) {
      return Promise.reject(ex);
    }
  }
}

const getUser = async(function* () {
    const user = yield findUser() // Async operation in a sync way
    console.log(`First Name: ${user.name}, LastName: ${user.lastName}`)
})

getUser() // First Name: Samuel, LastName: Castro



Async/Await (ES2017)

const dataBase = {
    id: 1,
    name: 'Samuel',
    lastName: 'Castro'
}
  
const findUser = () => {
    console.log('Finding user');
    
    return new Promise((resolve) => {
        setTimeout(() => resolve(dataBase), 3000);
    });
}
 

const getUser = async () => {
    const user = await findUser(); // Async operation in a awayc/await

    console.log(`First Name: ${user.name}, LastName: ${user.lastName}`);
}

getUser() // First Name: Samuel, LastName: Castro



Questions

Made with Slides.com