In one of my many deep-dives about JavaScript, I came across . They looked interesting. generators Then, I looked for some use-cases for generators. And looked. And looked. Eventually, I found a simple generator throttle example. After all this research, I resolved to see how I could use them. Since I was working on an Asynchronous JavaScript talk ( ), I wrote a state machine to facilitate positioning within the slide deck and managing font size on the presentation side. JavaScript Enjoys Your Tears What I found is documented here ... Generators are functions which can be exited and later re-entered. Their context (variable bindings) will be saved across re-entrances. - MDN. The ability of functions to be paused and then resumed again. A generator returns an iterator. On creation, the code inside the generator is not executed. Solves "reasoning about" issues. Allows for non-"run-to-completion" behavior. Localized blocking only. Syntactic form of a state machine. Cooperative concurrency versus preemptive concurrency. Advantages of Generators Lazy Evaluation This is an evaluation model which delays the evaluation of an expression until its value is needed. That is, if the value is not needed, it will not exist. It is calculated on demand. Memory Efficient A direct consequence of Lazy Evaluation is that generators are memory efficient. The only values generated are those that are needed. With normal functions, all the values must be pre-generated and kept around in case they need to be used later. However, with generators, computation is deferred. Use-Cases Here are some Generator use-cases ... Infinitely Repeating Array This is the article (by Shawn Reisner) that got me interested in this topic in the first place. A Quick, Practical Use Case for ES6 Generators Generating Unique Identifiers This is from a post (by Nick Scialli @nas5w): . TWEET An interesting use case for a javascript generator: generating an infinite number of unique identifiers! { i = ; ( ) i++; } ids = idCreator(); .log(ids.next().value); .log(ids.next().value); .log(ids.next().value); * ( ) function idCreator let 0 while true yield const console // 0 console // 1 console // 2 // etc ... Throttle Generator This generator will throttle a function for an amount of time (in milliseconds). { timerID = ; { clearTimeout(timerID); timerID = setTimeout(func.bind( , arg), time); } ( ) throttled( ); } { constuctor() {}; start = { thr = throttle( .log, ); thr.next( ); }; toString = { .log(throttle); .log( , .start); }; }; export * ( ) function throttle func, time let null ( ) function throttled arg window while true yield export class GeneratorThrottle => () console 3000 '' => () console console 'start =' this Content State-Machine { _content; _default; _statePatterns; _returnState; _changeAlgorithm; _machine; (settings) { ._content = settings.content; ._default = settings.defaultIndex; ._statePatterns = settings.statePatterns; ._returnState = settings.returnState; ._changeAlgorithm = settings.changeAlgorithm; machineSettings = { : ._content, : ._default, : ._statePatterns, : ._returnState }; ._machine = .stateMachine(machineSettings); ._machine; }; stateMachine = { content = settings.content; defaultIndex = settings.defaultIndex; statePatterns = settings.statePatterns; returnState = settings.returnState; currentIndex = defaultIndex; (currentIndex >= && currentIndex < content.length) { ( ._changeAlgorithm) { states = returnState(content, currentIndex); ._changeAlgorithm(states, currentIndex); } changeType = returnState(content, currentIndex); currentIndex = statePatterns[changeType](content, currentIndex); } }; } export class ContentStateMachine constructor this this this this this const 'content' this 'defaultIndex' this 'statePatterns' this 'returnState' this this this return this * ( ) function stateMachine settings const const const const let while 0 if this const this const yield Use as a font state-machine ... { ContentStateMachine } ; $( ).ready( { main = $( ); upButton = $( ); downButton = $( ); resetButton = $( ); channel = BroadcastChannel( ); actions = { : { upButton.hide(); downButton.hide(); resetButton.hide(); }, : { fontStateMachine.next( ); }, : { fontStateMachine.next( ); }, : { fontStateMachine.next( ); }, : { channel.postMessage({ : upButton.hasClass( ), : downButton.hasClass( ) }); } }; channel.onmessage = { actions[triggerAction.data](); }; sizes = [ , , , , , , , , , , ]; defaultIndex = .floor(sizes.length / ); changeFont = { ( i = , len = classes.length; i < len; i++) { (i === currentIndex) { main.addClass(classes[i]); } { main.removeClass(classes[i]); } } (currentIndex === ) { downButton.addClass( ); } { downButton.removeClass( ); } (currentIndex === classes.length - ) { upButton.addClass( ); } { upButton.removeClass( ); } actions[ ](); }; statePatterns = { : { max = content.length - ; (index + <= max) ? index + : index; }, : { (index - > ) ? index - : ; }, : { defaultIndex; } }; returnState = { content; }; settings = { : sizes, : defaultIndex, : statePatterns, : returnState, : changeFont }; fontStateMachine = ContentStateMachine(settings); fontStateMachine.next( ); upButton.on( , () => { actions[ ](); }); resetButton.on( , () => { actions[ ](); }); downButton.on( , () => { actions[ ](); }); }); import from '/scripts/presentation/_content-state-machine.js' document => () const '.main' const '.up' const '.down' const '.reset' const new 'le-slides-font-size' const init => () 'trigger-up' => () 'up' 'trigger-reset' => () 'reset' 'trigger-down' => () 'down' 'report-states' => () upDisabled 'disabled' downDisabled 'disabled' ( ) => triggerAction const 'fsm05' 'fsm04' 'fsm03' 'fsm02' 'fsm01' 'fs00' 'fsp01' 'fsp02' 'fsp03' 'fsp04' 'fsp05' const Math 2 const ( ) => classes, currentIndex for var 0 if else if 0 'disabled' else 'disabled' if 1 'disabled' else 'disabled' 'report-states' const 'up' ( ) => content, index const 1 return 1 1 'down' ( ) => content, index return 1 0 1 0 'reset' ( ) => content, index return const ( ) => content, currentIndex return const 'content' 'defaultIndex' 'statePatterns' 'returnState' 'changeAlgorithm' const new 'reset' 'click' 'trigger-up' 'click' 'trigger-reset' 'click' 'trigger-down' Use as a navigation state-machine ... { ContentStateMachine } ; $( ).ready( { $( ).load( , { slideStateMachine; nextButton = $( ); previousButton = $( ); channel = BroadcastChannel( ); actions = { : { nextButton.hide(); previousButton.hide(); }, : { slideStateMachine.next( ); }, : { slideStateMachine.next( ); }, : { channel.postMessage({ : index, : previousButton.hasClass( ), : nextButton.hasClass( ) }); } }; channel.onmessage = { actions[triggerAction.data](); }; cardData = []; cardTitles = []; $.getJSON( ) .done( { cardData = data; }) .fail( { .log( , data); (data.status!== ) { error = $( ).text( ); content.append(error); } }) .always( { (cardData.length > ) { initTitles(); } }); { ( i = , len = cardData.length; i < len; i++) { cardTitles.push(cardData[i].id); } init(); } { changeCurrentCard = { title = cards[currentIndex]; currentCard = $( ); previousTitle = (currentIndex - < ) ? : cardTitles[currentIndex - ]; nextTitle = (currentIndex + > maxCards - ) ? : cardTitles[currentIndex + ]; keep = [title]; currentCard.addClass( ); currentCard.attr( , ); (previousTitle.length > ) { keep.push(previousTitle); previousButton.removeClass( ); $( ) .attr( , ) .removeClass( ); } { previousButton.addClass( ); } (nextTitle.length > ) { keep.push(nextTitle); nextButton.removeClass( ); $( ) .attr( , ) .removeClass( ); } { nextButton.addClass( ); } $( ).text(currentIndex + ); actions[ ](currentIndex); ( i = , len = cards.length; i < len; i++) { element = $( ); (!keep.includes(cards[i])) { element.attr( , ); } } }; statePatterns = { : { (index - > ) ? index - : ; }, : { max = content.length - ; (index + <= max) ? index + : index; }, : { ; } }; returnState = { content; }; settings = { : cardTitles, : , : statePatterns, : returnState, : changeCurrentCard }; maxCards = cardTitles.length; $( ).text(maxCards); slideStateMachine = ContentStateMachine(settings); slideStateMachine.next( ); nextButton.on( , (event) => { actions[ ](); }); previousButton.on( , (event) => { actions[ ](); }); } }); }); import from '/scripts/presentation/_content-state-machine.js' document => () '.notes' '/templates/cards.html' ( ) function let const '.next' const '.previous' const new 'le-slides-position' const init => () 'trigger-previous' => () 'previous' 'trigger-next' => () 'next' 'report-states' ( ) => index currentIndex previousDisabled 'disabled' nextDisabled 'disabled' ( ) => triggerAction let let '/data/card-data.json' ( ) => data ( ) => data console 'fail' if 200 const '<div/>' 'Error loading JSON file' => () if 0 ( ) function initTitles for let 0 ( ) function init const ( ) => cards, currentIndex const const `.note[card=" "]` ${title} const 1 0 '' 1 const 1 1 '' 1 const 'slide' 'style' 'left:0;' if 0 'disabled' `[card=" "]` ${previousTitle} 'style' 'left:-100%;' 'slide' else 'disabled' if 0 'disabled' `[card=" "]` ${nextTitle} 'style' 'left:100%;' 'slide' else 'disabled' '.n' 1 'report-states' for let 0 const `[card=" "` ${cards[i]} if 'style' 'display:none;' const 'previous' ( ) => content, index return 1 0 1 0 'next' ( ) => content, index const 1 return 1 1 'reset' ( ) => content, index return 0 const ( ) => content, currentIndex return const 'content' 'defaultIndex' 0 'statePatterns' 'returnState' 'changeAlgorithm' const '.max' new 'reset' 'click' 'trigger-next' 'click' 'trigger-previous' Conclusions After a ton of research, I found very few practical examples of JavaScript Generators. I wanted to find ways use them. After working with them on an Asynchronous JavaScript talk ( ), I found a state machine to facilitate positioning within the slide deck and managing font size on the presentation side to be an excellent example. JavaScript Enjoys Your Tears Could I have managed state in other ways? Certainly. But, I wouldn't have learned nearly as much as I did with the code above. Also published at https://dev.to/rfornal/use-cases-for-javascript-generators-1npc