{"id":8126,"date":"2019-08-01T22:38:00","date_gmt":"2019-08-02T02:38:00","guid":{"rendered":"https:\/\/www.ericfeminella.com\/blog\/?p=8126"},"modified":"2024-02-23T13:49:47","modified_gmt":"2024-02-23T18:49:47","slug":"benefits-of-javascript-generators","status":"publish","type":"post","link":"https:\/\/www.ericfeminella.com\/blog\/2019\/08\/01\/benefits-of-javascript-generators\/","title":{"rendered":"Benefits of JavaScript Generators"},"content":{"rendered":"<p><img decoding=\"async\" src=\"\/img\/articles\/generators.png\" alt=\"JavaScript Symbols\"\/><\/p>\n<p>One of the more nuanced features introduced in <a href=\"http:\/\/es6-features.org\/\" target=\"_blank\" rel=\"noopener\">ES6<\/a> is that of <code>Generator functions<\/code>. <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/function*\" target=\"_blank\" rel=\"noopener\">Generators<\/a> offer a powerful, yet often misunderstood mechanism for controlling the flow of operations, allowing developers to implement solutions with improved readability and efficiency. This article briefly delves into a few of the benefits that JavaScript Generators have to offer, elucidating on their purpose, functionality, and specific scenarios which can benefit from their usage.<\/p>\n<h2 id=\"understanding-generators\" class=\"section-link\">\n <a href=\"#understanding-generators\">Understanding Generators<\/a><br \/>\n<\/h2>\n<p>A Generator function is a special type of function that can pause execution and subsequently resume at a later time, making it quite valuable for handling asynchronous operations as well as many other use cases. Unlike regular functions which run to completion upon invocation, Generator functions return an <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Iteration_protocols\" target=\"_blank\" rel=\"noopener\">Iterator<\/a> through which their execution can be controlled. It is important to note that while generators facilitate asynchronous operations, they do so by yielding Promises and require external mechanisms, such as <code>async\/await<\/code> or libraries, to handle the asynchronous resolution.<\/p>\n<h2 id=\"generator-syntax\" class=\"section-link\">\n <a href=\"#generator-syntax\">Generator Syntax and Operation<\/a><br \/>\n<\/h2>\n<p><code>Generators<\/code> are defined with the <code>function<\/code> keyword followed by an asterisk (<code>*<\/code>); i.e. <code>(function*)<\/code>, and are instantiated when called, but not executed immediately. Rather, they wait for the caller to request the next result. This is achieved using the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Iteration_protocols#the_iterator_protocol\" target=\"_blank\" rel=\"noopener\">Iterator.next()<\/a> method, which resumes execution until the next <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/yield\" target=\"_blank\" rel=\"noopener\">yield<\/a> statement is encountered, or the generator function returns.<\/p>\n<pre class=\"lang:js\">\nfunction* generatorExample() {\n  yield 'First';\n  yield 'Second';\n};\n\/\/ create the generator, then get the values ...\nconst generator = generatorExample();\nconsole.log(generator.next().value); \/\/ \"First\"\nconsole.log(generator.next().value); \/\/ \"Second\"\nconsole.log(generator.next().done); \/\/ true\n<\/pre>\n<h2 id=\"generator-iterators\" class=\"section-link\">\n  <a href=\"#generator-iterators\">Generator Iterators<\/a><br \/>\n<\/h2>\n<p>As mentioned, <code>Generator<\/code> functions return an <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Iteration_protocols\" target=\"_blank\" rel=\"noopener\">Iterator<\/a>, therefore, all functionality of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Iteration_protocols#the_iterable_protocol\" target=\"_blank\" rel=\"noopener\">Iterables<\/a> are available to them, such as <code>for...of<\/code> loops, <code>destructuring<\/code>, <code>...rest parameters<\/code>, etc.:<\/p>\n<pre class=\"lang:js\">\nfunction* generatorExample() {\n  yield 'First';\n  yield 'Second';\n};\n\n\/\/ for...of loops\nfor (const value of generatorExample()) {\n  console.log(value); \/\/ \"First\", \"Second\"\n}\n\n\/\/ array destructuring ...\nconst items = [...generatorExample(), 'Third'];\nconsole.log(items); \/\/ [\"First\", \"Second\", \"Third\"]\n\n\/\/ ...rest parameters \nconst updateItems = (...items) => {\n  console.log(items);\n};\nupdateItems(...generatorExample()); \/\/ \"First\", \"Second\"\n<\/pre>\n<h2 id=\"custom-iteration-logic\" class=\"section-link\">\n <a href=\"#custom-iteration-logic\">Custom Iterations<\/a><br \/>\n<\/h2>\n<p>Generators allow for the creation of custom iteration logic, such as generating sequences without the need to pre-calculate the entire set. For example, one can generate a Fibonacci sequence using generators as follows:<\/p>\n<pre class=\"lang:js\">\nfunction* fibonacci(limit) {\n  let [previous, current] = [0, 1];\n  while (limit-- > 0) {\n    [previous, current] = [current, previous + current];\n    yield current;\n  }\n};\n\/\/ each iteration yields the next number in the sequence ...\nfor (const num of fibonacci(6)) {\n  console.log(num); \/\/ 1, 2, 3, 5, 8, 13\n};\n<\/pre>\n<h2 id=\"stateful-iterations\" class=\"section-link\">\n <a href=\"#stateful-iterations\">Stateful Iterations<\/a><br \/>\n<\/h2>\n<p><code>Generators<\/code> have the ability to maintain state between <code>yields<\/code>, thus they are quite useful for managing stateful iterations. This feature can be leveraged in scenarios such as those which require pause and resume logic based on runtime conditions. For instance:<\/p>\n<pre class=\"lang:js\">\nconst gameState = function*() {\n  let gameOver = false;\n  let score = 0;\n  while (!gameOver) {\n    \/\/ receive, increment, and yield the updated score \n    \/\/ here, the \"score\" that is yielded is a reference\n    \/\/ to the value passed to .next \n    gameOver = (score += yield score) >= 10;\n  }\n  return 'Game Over'; \/\/ return 'Game Over' when the loop ends\n};\n\n\/\/ create the game generator ...\nconst game = gameState();\n \n\/\/ starting the game, the first call initializes the game \n\/\/ and returns the initial score ...\nconsole.log(game.next().value); \/\/ 0\n \n\/\/ simulate score increments ...\nconsole.log(game.next(1).value); \/\/ 1\nconsole.log(game.next(2).value); \/\/ 3\nconsole.log(game.next(3).value); \/\/ 6\nconsole.log(game.next(4).value); \/\/ \"Game Over\"\n \n\/\/ subsequent calls will continue to return undefined as\n\/\/ the game has completed ...\nconsole.log(game.next(10).value); \/\/ undefined\nconsole.log(game.next().done); \/\/ true\n<\/pre>\n<p>It may initially seem confusing as to how the value passed to <code>game.next(value)<\/code> is referenced within the Generator function. However, it is important to understand how this mechanism works as it is a core feature of generators, allowing them to interact dynamically with external input. Below is a breakdown outlining this behavior in the context of the above example:<\/p>\n<ol>\n<li><strong>Starting the Generator<\/strong>: When <code>game.next()<\/code> is first called, the <code>gameState<\/code> generator function begins execution until it reaches the first <code>yield<\/code> statement. This initial call starts the generator but does not yet pass any value into it, as the generator is not yet paused at a <code>yield<\/code> that could receive a value.<\/li>\n<li><strong>Pausing Execution<\/strong>: The <code>yield<\/code> statement pauses the generator&#8217;s execution and waits for the next input to be provided. This pausing mechanism is what differentiates generators from regular functions, allowing for a two-way exchange of values.<\/li>\n<li><strong>Resuming with a Value<\/strong>: After the generator is initiated and paused at a <code>yield<\/code>, calling <code>game.next(value)<\/code> resumes execution, passing the <code>value<\/code> into the generator. This passed value is received by the <code>yield<\/code> expression where the generator was paused.<\/li>\n<li><strong>Processing and Pausing Again<\/strong>: Once the generator function receives the value and resumes execution, it processes operations following the <code>yield<\/code> until it either encounters the next <code>yield<\/code> (and pauses again, awaiting further input), reaches a <code>return<\/code> statement (effectively ending the generator&#8217;s execution), or completes its execution block.<\/li>\n<\/ol>\n<p>This interactive capability of generators to receive external inputs and potentially alter their internal state or control flow based on those inputs is what makes them particularly powerful for tasks requiring stateful iterations or complex control flows.<\/p>\n<h2 id=\"generator-returns\" class=\"section-link\">\n  <a href=\"#generator-returns\">Generator Returns<\/a><br \/>\n<\/h2>\n<p>In addition to yielding values with <code>yield<\/code>, generators have a distinct behavior when it comes to the <code>return<\/code> statement. A <code>return<\/code> statement inside a generator function does not merely exit the function, but instead, it provides a value that can be retrieved by the iterator. This behavior allows generators to signal a final value before ceasing their execution.<\/p>\n<p>When a generator encounters a <code>return<\/code> statement, it returns an object with two properties: <code>value<\/code>, which is the value specified by the <code>return<\/code> statement, and <code>done<\/code>, which is set to <code>true<\/code> to indicate that the generator has completed its execution. This is different from the <code>yield<\/code> statement, which also returns an object but with <code>done<\/code> set to <code>false<\/code> until the generator function has fully completed.<\/p>\n<pre class=\"lang:js\">\nfunction* returnExampleGenerator() {\n  yield 'First';\n  return 'Finished';\n}\n\nconst generator = returnExampleGenerator();\nconsole.log(generator.next().value); \/\/ \"First\"\nconsole.log(generator.next()); \/\/ {value: \"Finished\", done: true}\n<\/pre>\n<p>This example illustrates that after the <code>return<\/code> statement is executed, the generator indicates it is done, and no further values can be yielded. However, the final value returned by the generator can be used to convey meaningful information or a result to the iterator, effectively providing a clean way to end the generator&#8217;s execution while also returning a value.<\/p>\n<p>Generators also provide a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Generator\/return#using_return\" target=\"_blank\" rel=\"noopener\">return()<\/a> method that can be used to terminate the generator&#8217;s execution prematurely. When <code>return()<\/code> is called on a generator object, the generator is immediately terminated and returns an object with a <code>value<\/code> property set to the argument provided to <code>return()<\/code>, and a <code>done<\/code> property set to <code>true<\/code>. This method is especially useful for allowing clients to cleanly exit generator functions, such as for ensuring resources are released appropriately, etc..<\/p>\n<pre class=\"lang:js\">\nfunction* generatorExample() {\n  yield 'First';\n  yield 'Second';\n}\n\nconst generator = generatorExample();\nconsole.log(generator.next().value); \/\/ Logs \"First\"\n\/\/ terminate the generator early ...\nconsole.log(generator.return('Last').value); \/\/ Logs \"Last\"\nconsole.log(generator.next()); \/\/ {value: undefined, done: true}\n<\/pre>\n<p>In this example, after the first yield is consumed, <code>return()<\/code> is invoked on the generator. This action terminates the generator, returns the provided value, and sets the <code>done<\/code> property of the generator to <code>true<\/code>, indicating that the generator has completed and will no longer <code>yield<\/code> values.<\/p>\n<p>This capability of generators to be terminated early and cleanly, returning a specified value, provides developers fine-grained control over generator execution.<\/p>\n<h2 id=\"error-handling\" class=\"section-link\">\n  <a href=\"#error-handling\">Error Handling in Generators<\/a><br \/>\n<\/h2>\n<p>Generators provide a robust mechanism for error handling, allowing errors to be thrown back into the generator&#8217;s execution context. This is accomplished using the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Generator\/throw\" target=\"_blank\" rel=\"noopener\">generator.throw()<\/a> method. When an error is thrown within a generator, the current <code>yield<\/code> expression is replaced by a throw statement, causing the generator to resume execution. If the thrown error is not caught within the generator, it propagates back to the caller.<\/p>\n<p>This feature is particularly useful for managing errors in asynchronous operations, enabling developers to handle errors in a synchronous-like manner within the asynchronous control flow of a generator.<\/p>\n<pre class=\"lang:js\">\nfunction* errorHandlingGenerator() {\n  try {\n    yield 'First';\n    \/\/ error thrown here will be caught by the catch block below\n    yield 'Second';\n  } catch (error) {\n    console.log('Caught error:', error.message);\n  }\n}\n\nconst generator = errorHandlingGenerator();\nconsole.log(generator.next().value); \/\/ \"First\"\n\/\/ Simulate an error\ngenerator.throw(new Error('Something went wrong'));\n<\/pre>\n<p>This example illustrates how <code>generator.throw()<\/code> can be used to simulate error conditions and test error handling logic within generators. It also shows how generators maintain their state and control flow, even in the presence of errors, providing a powerful tool for asynchronous error management.<\/p>\n<h2 id=\"generator-composition\" class=\"section-link\">\n <a href=\"#generator-composition\">Generator Composition<\/a><br \/>\n<\/h2>\n<p>One particularly interesting feature of <code>Generators<\/code> is that they can be composed of other generators via the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/yield*\" target=\"_blank\" rel=\"noopener\">yield*<\/a> operator.<\/p>\n<pre class=\"lang:js\">\nconst generator1 = function*() {\n  yield 1;\n  yield 2;\n  yield 3;\n};\nconst generator2 = function*() {\n  yield 'One';\n  yield 'Two';\n  yield 'Three';\n};\n\nconst generator = function*() {\n  yield* generator1();\n  yield* generator2();\n}\n \n\/\/ for...of loops\nfor (let value of generator()) {\n  console.log(value); \/\/ 1, 2, 3, \"One\", \"Two\", \"Three\"\n}\n<\/pre>\n<p>The ability to compose <code>Generators<\/code> allows for implementing various levels of abstraction and reuse, making their usage much more flexible. <\/p>\n<h2 id=\"conclusion\" class=\"section-link\">\n  <a href=\"#conclusion\">Concluding Thoughts<\/a><br \/>\n<\/h2>\n<p><code>Generators<\/code> can be used for many purposes, ranging from basic use-cases such as generating a sequence of numbers, to more complex scenarios such as handling streams of data so as to allow for processing input as it arrives. Through the brief examples above, we&#8217;ve seen how Generators can improve the way we, as developers, approach implementing solutions for asynchronous programming, iteration, and state management. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the more nuanced features introduced in ES6 is that of Generator functions. Generators offer a powerful, yet often misunderstood mechanism for controlling the flow of operations, allowing developers to implement solutions with improved readability and efficiency. This article briefly delves into a few of the benefits that JavaScript Generators have to offer, elucidating on their purpose, functionality, and&#8230; <a class=\"read-more\" href=\"https:\/\/www.ericfeminella.com\/blog\/2019\/08\/01\/benefits-of-javascript-generators\/\">Continue Reading<\/a><\/p>\n","protected":false},"author":1,"featured_media":8183,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[42,246,78],"tags":[166,297,261,358,354,356,357,68,355],"class_list":["post-8126","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-apis","category-es6","category-javascript-2","tag-browsers-2","tag-destructuring","tag-es6","tag-for-in","tag-generators","tag-iterables","tag-iterators","tag-javascript","tag-yield"],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.ericfeminella.com\/blog\/wp-content\/uploads\/2018\/04\/generators.png?fit=718%2C320&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts\/8126","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/comments?post=8126"}],"version-history":[{"count":0,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/posts\/8126\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/media\/8183"}],"wp:attachment":[{"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/media?parent=8126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/categories?post=8126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ericfeminella.com\/blog\/wp-json\/wp\/v2\/tags?post=8126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}