Easy Tutorial
❮ Cpp String Array Pointer Android Tutorial Listen Event Handle ❯

5.2 ES6 Generator Function

Category ES6 Tutorial

ES6 introduced the Generator function, which can suspend the flow of function execution using the yield keyword, providing a way to alter execution flow and offering a solution for asynchronous programming.

Basic Usage

Generator Function Composition

A Generator function differs from a regular function in two aspects:

The asterisk is used to denote that the function is a Generator function, and yield is used to define the internal states of the function.

function* func(){
 console.log("one");
 yield '1';
 console.log("two");
 yield '2'; 
 console.log("three");
 return '3';
}

Execution Mechanism

Calling a Generator function is similar to calling a regular function, by appending () after the function name. However, unlike regular functions, a Generator function does not execute immediately. Instead, it returns a pointer to the internal state object. To execute the function, you need to call the next method of the iterator object, which starts execution from the beginning of the function or from the last paused point.

f.next();
// one
// {value: "1", done: false}

f.next();
// two
// {value: "2", done: false}

f.next();
// three
// {value: "3", done: true}

f.next();
// {value: undefined, done: true}

The first call to next starts execution from the beginning of the Generator function, prints "one", and pauses at the first yield, returning an object with the value of the expression following yield and done set to false.

The second call to next resumes execution from the previous pause point, and the process repeats.

The third call prints "three", executes the return statement, and returns an object with the value of the return expression and done set to true.

The fourth call returns an object with value set to undefined and done set to true, indicating the function has completed execution.

Methods of the Iterator Object Returned by the Function

next Method

When next is called without parameters, the return value of yield is undefined. When next is called with a parameter, that parameter becomes the return value of the previous yield.

function* sendParameter(){
    console.log("start");
    var x = yield '2';
    console.log("one:" + x);
    var y = yield '3';
    console.log("two:" + y);
    console.log("total:" + (x + y));
}

Without parameters:

var sendp1 = sendParameter();
sendp1.next();
// start
// {value: "2", done: false}
sendp1.next();
// one:undefined
// {value: "3", done: false}
sendp1.next();
// two:undefined
// total:NaN
// {value: undefined, done: true}

With parameters:

var sendp2 = sendParameter();
sendp2.next(10);
// start
// {value: "2", done: false}
sendp2.next(20);
// one:20
// {value: "3", done: false}
sendp2.next(30);
// two:30
// total:50
// {value: undefined, done: true}

return Method

The return method returns a given value and terminates the iteration of the Generator function.

If a parameter is provided to return, it is returned; otherwise, undefined is returned.

function* foo(){
    yield 1;
    yield 2;
    yield 3;
}
var f = foo();
f.next();
// {value: 1, done: false}
f.return("foo");
// {value: "foo", done: true}
f.next();
// {value: undefined, done: true}

throw Method

The throw method can throw an exception outside the Generator function, which can be caught inside the function.

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('catch inner', e);
  }
};

var i = g();
i.next();

try {
  i.throw('a');
  i.throw('b');
} catch (e) {
  console.log('catch outside', e);
}
// catch inner a
// catch outside b

yield* Expression

The yield* expression is used to yield an iterator object, allowing one Generator function to call another within its body.

function* callee() {
    console.log('callee: ' + (yield));
}
function* caller() {
    while (true) {
        yield* callee();
    }
}
const callerObj = caller();
callerObj.next();
// {value: undefined, done: false}
callerObj.next("a");
// callee: a
// {value: undefined, done: false}
callerObj.next("b");
// callee: b
// {value: undefined, done: false}

// Equivalent to
function* caller() {
    while (true) {
        for (var value of callee) {
          yield value;
        }
    }
}

Use Cases

Implementing Iterator

Providing iteration methods for objects that do not natively support the Iterator interface.

function* objectEntries(obj) {
    const propKeys = Reflect.ownKeys(obj);
    for (const propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}

const jane = { first: 'Jane', last: 'Doe' };
for (const [key,value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

Reflect.ownKeys() returns all properties of an object, including non-enumerable properties and Symbols.

The jane object does not natively support the Iterator interface and cannot be iterated using for...of. By using a Generator function, we add the Iterator interface to jane, enabling iteration.

** Share Your Notes

Cancel

-

-

-

-1.1 ES6 Tutorial

-1.2 ES6 Environment Setup

-2.1 ES6 let and const

-2.2 ES6 Destructuring Assignment

-2.3 ES6 Symbol

-3.1.1 ES6 Map and Set

-3.1.2 ES6 Reflect and Proxy

-3.2.1 ES6 Strings

-3.2.2 ES6 Numbers

-3.2.3 ES6 Objects

-3.2.4 ES6 Arrays

-4.1 ES6 Functions

-4.3 ES6 Class

-4.4 ES6 Modules

-5.1 ES6 Promise Object

-5.3 ES6 async Function

Follow on WeChat

English:

❮ Cpp String Array Pointer Android Tutorial Listen Event Handle ❯