JavaScript Generators vs. Functions: Unraveling the Differences

Emir AKAR
ÇSTech
Published in
4 min readOct 27, 2023
Generators and Functions

JavaScript is a versatile and dynamic programming language that offers a variety of tools and features to developers. Two essential concepts for controlling the flow of execution in JavaScript are regular functions and generators. In this article, we will explore the differences between these two and when to use each, shedding light on their unique capabilities and use cases.

Functions: The Building Blocks

Functions are the fundamental building blocks of any JavaScript application. They are reusable blocks of code that can take parameters, perform actions, and return values. Functions can be defined using the function keyword or as arrow functions introduced in ECMAScript 6 (ES6).

Here’s a simple example of a regular function:

function add(a, b) {
return a + b;
}

Generators: A New Dimension

Generators are also added to JavaScript, introduced with ES6. They provide a way to pause and resume the execution of a function, offering a unique mechanism for handling asynchronous tasks and complex data streams.

A generator function is defined using an asterisk (*) after the function keyword and uses the yield statement to pause its execution and yield a value.

Here's a simple generator function:

function* count() {
yield 1;
yield 2;
yield 3;
}

Key Differences

Execution Control:

  • Regular functions run to completion, executing all statements in one go.
  • Generator functions can pause their execution with the yield keyword and later resume from where they left off, allowing for cooperative multitasking.

Iterations:

One of the powerful features of generators is their natural compatibility with iteration. You can use generators to create custom iterators for data structures or data streams.

Here’s an example of an iterable object using a generator:

function* fibonacciSequence() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}

const fibonacciIterator = fibonacciSequence();

// Let's print the first 10 Fibonacci numbers
for (let i = 0; i < 10; i++) {
console.log(fibonacciIterator.next().value);
}

In this example, fibonacciSequence is a generator function that generates an infinite Fibonacci sequence. It uses destructuring to swap the values of a and b to calculate the next Fibonacci number. The generator is used to create an iterator, and the for loop retrieves the first 10 Fibonacci numbers. The generator naturally handles an infinite sequence, allowing you to extract values on-the-fly.

Async Operations:

Generators are especially helpful in managing asynchronous operations. They simplify asynchronous code by providing a more linear and readable structure. Here’s an example of using a generator to handle asynchronous operations, such as making multiple API requests:

function* fetchMultipleAPIs() {
try {
const result1 = yield fetch('https://api.com/endpoint1');
const data1 = yield result1.json();

const result2 = yield fetch('https://api.com/endpoint2');
const data2 = yield result2.json();

console.log(data1, data2);
} catch (error) {
console.error('Error:', error);
}
}

// Using a helper function to run the generator
function run(generator) {
const iterator = generator();

function iterate(iteration) {
if (iteration.done) {
return iteration.value;
}

const promise = iteration.value;

return promise
.then((x) => iterate(iterator.next(x)))
.catch((x) => iterate(iterator.throw(x)));
}

return iterate(iterator.next());
}

run(fetchMultipleAPIs);

In this example, fetchMultipleAPIs is a generator that fetches data from two different endpoints and logs the results. The run function is a utility to manage the generator's execution, handling both successful and error scenarios.

When to Use Which Type of Function

  1. Use regular functions for synchronous tasks, simple calculations, and logic that doesn’t require asynchronous behavior.
  2. Use generators when you need to handle complex asynchronous operations, work with data streams, or manage long-running tasks. Generators simplify asynchronous code by making it more readable and maintainable.
  3. Consider a combination of both in your codebase. Regular functions handle your core business logic, while generators manage the complex, asynchronous parts of your application.

Keep in Mind

For the best practices, it’s good to keep in mind that every feature has its own pros and cons. For that reasons, you should consider about the cons such as;

  1. Potential Memory Issues: Generators can hold the state and local variables in memory between `yield` points. If not managed properly, this can lead to memory leaks, especially in long-running generators.
  2. Debugging Challenges: Debugging generator functions can be more challenging, as they don’t execute linearly like regular functions, making it harder to trace the flow of code.

Conclusion

JavaScript functions and generators are both essential tools in a developer’s toolkit, each with its unique strengths and use cases. Regular functions are suitable for most of your synchronous needs, while generators shine when dealing with asynchronous operations, data streams, and even infinite sequences. By understanding the differences and choosing the right tool for the job, you can write more efficient and maintainable JavaScript code.

--

--

Emir AKAR
ÇSTech
Writer for

Hi there! I am Emir. I am a curious Frontend Developer who likes to improve himself in different fields. I like using JavaScript, HTML, CSS, and React JS.