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:
- Generator functions are defined using the
function*
declaration syntax instead of thefunction
keyword for ordinary functions. - The
yield
statement can be used inside the generator function to suspend the execution of the function and return a value. - A generator function can return values multiple times, one at a time.
- Generator functions can use the
return
statement to end the execution of the function and return a final value. - Generator functions can use the
throw
statement to throw an exception. - Generator functions can iterate over the resulting values via a
for...of
loop.
Usage scenario
Generator functions can be used in the following scenarios:
- Asynchronous programming: Asynchronous programming patterns such as coroutines and generator patterns can be easily implemented using generator functions.
- 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.
- 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
- 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.
- 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.
- 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
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.