# what the # heck is # gimgen? ---- George Mauer @togakangaroo - [http://georgemauer.net](http://georgemauer.net)
Me ⮕
I'm CTO at
Operation Spark
You, after this talk
OR
Iterators are Simple(ish)
const manualFibbonacci = () => { let prev1 = 1, prev2 = 1, step = 0 return { next: () => { if(step > 100) return {value: undefined, done: true}; step += 1 if(step <= 2) return {value: 1, done: false}; [prev1, prev2] = [prev2, prev1 + prev2] return {value: prev2, done: false}; } } }
Run
const fib = manualFibbonacci() console.log(fib.next()) console.log(fib.next()) console.log(fib.next()) console.log(fib.next())
Iterators can be...well...iterated
Run
const fibIterator = manualFibbonacci() let x while((x = fibIterator.next(), !x.done)) console.log(x.value)
Iterators can be used in loops
Run
const thingToIterate = {} thingToIterate[Symbol.iterator] = manualFibbonacci for(let val of thingToIterate) console.log(val)
Yup, you can make anything iterable!
Lets talk about ducks
const duck = (name, children=[]) => ({name, children}) const fergus = duck("Fergus", [ duck("Matilda"), duck("Scrooge"), duck("Hortense", [ duck("Donald"), duck("Della", [ duck("Huey"), duck("Dewey"), duck("Louie"), ]) ]), ])
Iterators can flatten
Run
const getAllManual = (root) => { const stack = [root] return { next: () => { if(!stack.length) return {value: undefined, done: true} const value = stack.pop() for(let c of value.children) stack.push(c) return {value, done: false} } } } fergus[Symbol.iterator] = () => getAllManual(fergus) for(let {name} of fergus) console.log(name)
generators are a simpler iterator syntax
const manualfibbonacci = () => { let prev1 = 1, prev2 = 1, step = 0 return { next: () => { if(step > 100) return {value: undefined, done: true}; step += 1 if(step <= 2) return {value: 1, done: false}; [prev1, prev2] = [prev2, prev1 + prev2] return {value: prev2, done: false}; } } }
==
run
const fibonacci = function * () { let prev1 = 1, prev2 = 1 yield prev1 yield prev2 for(let i = 0; i < 98; i+=1) { [prev1, prev2] = [prev2, prev1 + prev2] yield prev2 } } for(let v of fibonacci()) console.log(v)
Oh yeah, iterators can be infinite!
run
const manualFibbonacci = () => { let prev1 = 1, prev2 = 1, step = 0 return { next: () => { step += 1 if(step <= 2) return {value: 1, done: false}; [prev1, prev2] = [prev2, prev1 + prev2] return {value: prev2, done: false}; } } } const it = manualFibbonacci() for(let i = 0; i < 100; i+=1) it.next() console.log(it.next())
==
run
const fibbonacci = function * () { let prev1 = 1, prev2 = 1 yield prev1 yield prev2 while(true) { [prev1, prev2] = [prev2, prev1 + prev2] yield prev2 } } const it = fibbonacci() for(let i = 0; i < 100; i+=1) it.next() console.log(it.next())
Flattening ducks with generators
Run
const getAll = function * (root) { yield root for(let c of root.children) yield * getAll(c) } for(let {name} of getAll(fergus)) console.log(name)
Infinite generators are streams
const incrementingRandoms = function * () { const rand = (max, min=0) => Math.floor(Math.random() * (max - min)) + min let prev = 0 while(true) { prev = rand(prev+1000, prev+1) yield prev } }
run
const it = incrementingRandoms() for(let i = 0; i < 10; i+=1) console.log(it.next().value)
You choose how to enumerate iterators
run
const logAndTakeEveryMs = (ms, createIterator) => { const it = createIterator() const logAndGetNext = ({done, value}) => { if(done) return; console.log(value) setTimeout( () => logAndGetNext(it.next()) , ms) } logAndGetNext(it.next()) } logAndTakeEveryMs(500, incrementingRandoms)
Now with...promises!
run
const delayedIncrementingRandoms = function * () { const rand = (max, min=0) => Math.floor(Math.random() * (max - min)) + min let val = 0 while(true) { val = rand(val+1000, val+1) console.log("Currently", val) yield new Promise(resolve => setTimeout(() => resolve(val), 300) ) } } const runAsync = (createIterator) => { const it = createIterator() const runNext = ({done, value: promise}) => { if(done) return; promise.then(() => runNext(it.next())) } runNext(it.next()) } runAsync(delayedIncrementingRandoms)
You can use this with anything async
const async = (createIterator) => (...args) => runAsync(...args) const get = async(function * (url) { const result = yield fetch(url) return result.json() }) const showTopRecords = async(function * () { const topRecords = yield get('/top-records') const top3Records = topRecords.slice(0, 3) const gettingTop3Details = top3Records.map((id) => get(`/top-records/details/${id}`)) const topDetails = yield Promise.all(gettingTop3Details) const detailsHtml = template(topDetails) $container.detailsHtml(detailsHtml) })
Async...await was clearly a breakthrough
const get = async(function * (url) { const result = yield fetch(url) return result.json() }) const showTopRecords = async(function * () { const topRecords = yield get('/top-records') const top3Records = topRecords.slice(0, 3) const gettingTop3Details = top3Records.map((id) => get(`/top-records/details/${id}`)) const topDetails = yield Promise.all(gettingTop3Details) const detailsHtml = template(topDetails) $container.detailsHtml(detailsHtml) })
const get = async function (url) { const result = await fetch(url) return result.json() } const showTopRecords = async function () { const topRecords = await get('/top-records') const top3Records = topRecords.slice(0, 3) const gettingTop3Details = top3Records.map((id) => get(`/top-records/details/${id}`)) const topDetails = await Promise.all(gettingTop3Details) const detailsHtml = template(topDetails) $container.detailsHtml(detailsHtml) }
Requisite Functional Reactive Programming bubble chart
From this Egghead Tutorial
In search of a better debounce
Click Here to Message
How a debounced action works
Wait for button click
Start a timer and wait for either it or button click
If the next thing that happens is another click, start waiting for tick or click again
Run action
Start over
How a debounced action is implemented
let currentTimeout = null const btn = document.querySelector('#debounce-target') btn.addEventListener('click', () => { if(currentTimeout) clearTimeout(currentTimeout) currentTimeout = setTimeout(() => { console.log("You clicked it then stopped!") }, 2000) })
And here's how underscore does a generic one
_.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; };
What is even going on!?
Finally, some gimgen
Click Here to Message
Wait for button click
Start a timer and wait for either it or button click
If the next thing that happens is another click, start waiting for tick or click again
Run action
Start over
const { runGimgen, domEventToSignal, anySignal, timeoutSignal } = gimgen runGimgen(function * () { const tick = timeoutSignal(2000) const btn = document.querySelector('#debounce-gimgen-target') const click = domEventToSignal(btn, 'click') const tickOrClick = anySignal(tick, click) while(true) { yield click while(click == (yield tickOrClick).signal) {} console.log("Gimgen says you clicked some stuff a while ago") } })
You get to use some weird primitives
Click Here to Message
Wait for button click
Start a timer and wait for either it or button click
If the next thing that happens is another click, start waiting for tick or click again
Run action
Start over
const { runGimgen, domEventToSignal, anySignal, timeoutSignal } = gimgen runGimgen(function * () { const tick = timeoutSignal(2000) const btn = document.querySelector('#debounce-gimgen-target2') const click = domEventToSignal(btn, 'click') const tickOrClick = anySignal(tick, click) while(true) { yield click; while(true) { const {signal} = yield tickOrClick if(tick == signal) break; } console.log("Gimgen says you clicked some stuff a while ago"); } })
The same stream
Click Here to Message Multiple Clicks in 2 seconds
const throttleAndAggregate = (ms, signalToThrottle) => controlSignal(function * ({emit}) { const tick = timeoutSignal(ms) let throttledSinceTick = [] while(true) { const {signal, result} = yield anySignal(tick, signalToThrottle) if(signalToThrottle == signal) throttledSinceTick = [...throttledSinceTick, result] else if(tick == signal && throttledSinceTick.length) { const toEmit = throttledSinceTick throttledSinceTick = [] emit(toEmit) } } }) const throttledMultipleClicks = throttleAndAggregate( 2000, domEventToSignal(document.querySelector('#multiple-clicks-button'), 'click') ) const moreThanTwoThrottledClicks = controlSignal(function * ({emit}) { while(true) { const clicks = yield throttledMultipleClicks; if(clicks.length > 2) emit(clicks.length) } }) runGimgen(function * () { while(true) { const clickCount = yield moreThanTwoThrottledClicks console.log(`received ${clickCount} clicks`) } })
# Let's look at more examples --- [Demos](http://georgemauer.net/gimgen/) [Library and Docs](https://github.com/togakangaroo/gimgen/)
I don't know what I have here