Generator functions and yield in JavaScript

This article has participated in the “Newcomer Creation Ceremony” activity, and together we will start the road to gold nugget creation.

Generator functions and yield statements

A generator function is a special type of function in JavaScript that produces multiple values incrementally, rather than returning all the results at once. Unlike ordinary functions, generator functions can suspend their execution and resume execution when needed.

Generator functions are defined using the function* declaration syntax instead of the function keyword for ordinary functions. In a generator function, use the yield statement to indicate the execution flow of the function. The yield statement can pause the execution of the function and return a value.

Here is an example of a simple generator function that produces a sequence of numbers:

function* numberSequence() {<!-- -->
  let i = 1;
  while (true) {<!-- -->
    yield i ++ ;
  }
}

const generator = numberSequence();
console.log(generator.next().value); // 1
console.log(generator.next().value); // 2
console.log(generator.next().value); // 3
console.log(generator.next().value); // 4

In this example, we define a generator function numberSequence() that progressively yields a sequence of numbers in an infinite loop using yield statements. We created a generator object generator and called the next() method on it. Each call to the next() method will start from the previous Execution begins at the yield statement and pauses at the next yield statement.

Generator functions have the following characteristics:

  1. Generator functions are defined using the function* declaration syntax instead of the function keyword for ordinary functions.
  2. The yield statement can be used inside the generator function to suspend the execution of the function and return a value.
  3. A generator function can return values multiple times, one at a time.
  4. Generator functions can use the return statement to end the execution of the function and return a final value.
  5. Generator functions can use the throw statement to throw an exception.
  6. Generator functions can iterate over the resulting values via a for...of loop.

Usage scenario

Generator functions can be used in the following scenarios:

  1. Asynchronous programming: Asynchronous programming patterns such as coroutines and generator patterns can be easily implemented using generator functions.
  2. Handling large amounts of data: Instead of loading large amounts of data into memory all at once, use a generator function to fetch data incrementally from an external data source.
  3. State machines: State machines such as finite state machines (FSM) or pushdown automata (PDA) can be easily implemented using generator functions.

In summary, generator functions are a very useful type of function in JavaScript that can make programs more flexible, extensible, and easy to understand. It enables the function to generate multiple values, and can control the execution process of the function, making the function more interactive and controllable.

Using scene implementation code

  1. Asynchronous Programming: Using Generator Functions to Implement Coroutines

Generator functions are often used in asynchronous programming and are especially useful when implementing coroutines. In asynchronous programming, a program can perform other tasks while waiting for certain operations to complete, improving performance and responsiveness.

Here’s a simple example that demonstrates how to use a generator function to implement a coroutine, which suspends execution while waiting for an asynchronous operation to complete, and resumes execution when the operation completes:

function* myCoroutine() {<!-- -->
  const result1 = yield asyncOperation1();
  const result2 = yield asyncOperation2(result1);
  const result3 = yield asyncOperation3(result2);
  return result3;
}

const generator = myCoroutine();
generator.next().value.then(result1 => {<!-- -->
  return generator.next(result1).value;
}).then(result2 => {<!-- -->
  return generator.next(result2).value;
}).then(result3 => {<!-- -->
  console.log(result3);
});

In this example, we define a coroutine myCoroutine(), which performs a series of asynchronous operations, and pauses execution each time it waits for the completion of the asynchronous operation, and uses yield The code> statement returns the result of an asynchronous operation. We created a generator object generator and called the next() method on it. Each call to the next() method will start from the previous Execution begins at the yield statement and pauses at the next yield statement. We use the Promise’s then() method to handle the result of the asynchronous operation and pass the result to the next yield statement.

The benefit of using generator functions to implement coroutines is that it can make asynchronous programming easier and more intuitive. Using generator functions, we can organize asynchronous operations into sequential code without using complex programming patterns such as callback functions or Promise chain calls.

  1. Handling Large Volumes of Data: Reading Large Data Incrementally

Generator functions are also great for working with large amounts of data. Using a generator function, we can incrementally fetch data from an external data source, rather than loading large amounts of data into memory all at once. This improves the efficiency and scalability of the program.

Here is a simple example that demonstrates how to use a generator function to read data line by line from a file:

function* readLines(file) {<!-- -->
  const stream = fs.createReadStream(file);
  const reader = readline.createInterface({<!-- -->
    input: stream,
    crlfDelay: Infinity
  });

  for await (const line of reader) {<!-- -->
    yield line;
  }
}

const lineGenerator = readLines('large-file.txt');
for (let line of lineGenerator) {<!-- -->
  console. log(line);
}

In this example, we define a generator function readLines() that reads data line by line from a file and returns each line using the yield statement. We create a generator object lineGenerator and iterate over it, each iteration fetching a new line of data and processing it. This method can avoid loading a large amount of data into the internal memory at one time, but gradually obtains data from external data sources, thereby improving the efficiency and scalability of the program.

  1. InfiniteSequence: Generate an infinite sequence

Generator functions can also be used to generate infinite sequences. For example, we can use a generator function to generate the Fibonacci sequence:

function* fibonacci() {<!-- -->
  let a = 0, b = 1;
  while (true) {<!-- -->
    yield a;
    [a, b] = [b, a + b];
  }
}

const generator = fibonacci();
for (let i = 0; i < 10; i ++ ) {<!-- -->
  console.log(generator.next().value);
}

In this example, we define a generator function fibonacci() that uses a while loop to continuously generate Fibonacci numbers and uses yield statement returns each number. We create a generator object generator and iterate over it, each iteration getting a new number and processing it. Since generator functions can generate sequences infinitely, we need to specify the number of elements to generate when iterating.

The advantage of using a generator function to generate an infinite sequence is that it makes it easier for us to generate sequences of arbitrary length and avoids generating a large amount of data at once.

Comparison of yield and async await

Both yield and async/await are asynchronous programming techniques in JavaScript. They can be used to implement coroutines, but their usage and implementation methods are slightly different. There are different.

yield is used to define the iterator object in the generator function. Through the yield statement, the control can be returned to the caller of the generator, so that the generator can be used when needed Suspend execution. The generator can resume execution if needed, and continue executing the rest of the code. yield is mainly used for synchronous code in the generator function, it can convert synchronous code into asynchronous code, thus realizing coroutine.

async/await is an asynchronous programming mode introduced in the ECMAScript 2017 standard, which is based on the Promise object and uses the keywords async and await to implement Writing and executing asynchronous code. The async keyword is used to define an asynchronous function, which returns a Promise object, and the await keyword is used to wait for the completion of an asynchronous operation, thus avoiding the callback function Nesting and code readability issues.

Compared with yield, async/await is more intuitive, easy to understand and use. Using async/await can make the code more concise and clear, and can better handle errors and exceptions of asynchronous code. However, compared with yield, the implementation of async/await is more complicated, and the asynchronous code needs to be converted into Promise objects at compile time, so it may be possible in some older browsers Not supported.

Generally speaking, both yield and async/await are effective ways to implement asynchronous programming. They have their own advantages and disadvantages, and you can choose to use them according to the specific situation. If you need to implement more complex asynchronous operations, it is recommended to use async/await; if you need to implement some simple asynchronous operations, you can use yield to implement them.

syntaxbug.com © 2021 All Rights Reserved.