JavaScript Generator Functions
An under-rated and over-powered feature that can do wonders
In JavaScript, a generator function is a function that can be paused in the middle of its execution and resumed later, allowing it to produce a sequence of values over time, rather than producing all of its values at once like a regular function. This can be useful for a variety of tasks, such as producing a sequence of numbers, iterating over a data structure, or creating an infinite stream of data.
To create a generator function, you use the function*
syntax instead of the regular function
syntax. Inside the generator function, you can use the yield
keyword to pause the function and produce a value. Here's an example of a simple generator function that produces a sequence of numbers:
function* countFrom(n) {
while (true) {
yield n;
n++;
}
}
const counter = countFrom(1);
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2
console.log(counter.next().value); // 3
Each time you call the next
method on the generator object returned by the generator function, the function will resume execution from the point where it was paused and produce the next value in the sequence. When the generator function reaches the end of its execution, it will return an object with a done
property set to true
, indicating that the generator has completed its sequence.
Generator functions are often used in conjunction with iterators and the for-of
loop to iterate over a sequence of values. Here's an example of using a generator function to create an iterator that produces the Fibonacci sequence:
function* fibonacci() {
let [prev, curr] = [0, 1];
while (true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}
for (const n of fibonacci()) {
console.log(n);
if (n >= 1000) {
break;
}
}
Another great use of generator functions is to create form wizards. A form wizard is a user interface that allows a user to complete a multi-step form by navigating through a series of pages, or “steps,” one at a time. You can use a generator function to create a form wizard by creating a generator function that produces the steps of the form as a sequence of values and using the next
method to navigate between steps.
Here’s an example of how you might create a form wizard using a generator function:
function* formWizard() {
// Step 1: Collect personal information
const personalInfo = yield {
title: "Personal Information",
fields: [
{ label: "Full Name", type: "text" },
{ label: "Email Address", type: "email" },
{ label: "Phone Number", type: "tel" },
],
};
// Step 2: Collect address information
const addressInfo = yield {
title: "Address Information",
fields: [
{ label: "Street Address", type: "text" },
{ label: "City", type: "text" },
{ label: "State", type: "text" },
{ label: "Zip Code", type: "text" },
],
};
// Step 3: Review and submit form
const review = yield {
title: "Review and Submit",
fields: [
{
label: "Review your information",
type: "review",
data: { personalInfo, addressInfo },
},
],
};
// Return the completed form data
return { personalInfo, addressInfo, review };
}
const wizard = formWizard();
let step;
while (!(step = wizard.next()).done) {
// Render the current step of the form wizard
renderStep(step.value);
// Wait for the user to submit the current step
const formData = await getFormData();
// Pass the form data to the generator function
wizard.next(formData);
}
// The form wizard is complete
const formData = step.value;
submitForm(formData);
In this example, the generator function defines three steps in the form wizard: personal information, address information, and review and submit. Each time the next
method is called on the generator object, the generator function produces the next step in the form as an object with a title
and a list of fields
. The form data for each step is passed to the generator function as an argument to the next
method, and the final form data is returned by the generator function when it completes.