Skip to content

Instantly share code, notes, and snippets.

@amonks
Last active December 28, 2016 21:31
Show Gist options
  • Select an option

  • Save amonks/b06302fc5489d28b243e627fc2a910b4 to your computer and use it in GitHub Desktop.

Select an option

Save amonks/b06302fc5489d28b243e627fc2a910b4 to your computer and use it in GitHub Desktop.
// ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
// ⚠️ This isn't very written yet! ⚠️
// ⚠️ I'll post on monks.co when ⚠️
// ⚠️ it's more done. ⚠️
// ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
// check out this gist to see a changelog:
// https://gist.github.com/amonks/b06302fc5489d28b243e627fc2a910b4/
/* global R */
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
88888888888 88 88
88 ,d "" 88
88 88 88
88aaaaa 88 88 8b,dPPYba, ,adPPYba, MM88MMM 88 ,adPPYba, 8b,dPPYba, ,adPPYYba, 88
88""""" 88 88 88P' `"8a a8" "" 88 88 a8" "8a 88P' `"8a "" `Y8 88
88 88 88 88 88 8b 88 88 8b d8 88 88 ,adPPPPP88 88
88 888a, ,a88 88 88 "8a, ,aa 88, 88 "8a, ,a8" 88 888888, ,88 88
88 88`"YbbdP'Y8 88 88 `"Ybbd8"' "Y888 88 `"YbbdP"' 88 88""`"8bbdP"Y8 88 ,d
88 88
88 ,adPPYYba, 8b d8 ,adPPYYba, ,adPPYba, ,adPPYba, 8b,dPPYba, 88 8b,dPPYba, MM88MMM 888
88 "" `Y8 `8b d8' "" `Y8 I8[ "" a8" "" 88P' "Y8 88 88P' "8a 88 888
88 ,adPPPPP88 `8b d8' ,adPPPPP88 `"Y8ba, 8b 88 88 88 d8 88
88, ,d88 88, ,88 `8b,d8' 88, ,88 aa ]8I "8a, ,aa 88 88 88b, ,a8" 88, 888
"Y8888P" `"8bbdP"Y8 "8" `"8bbdP"Y8 `"YbbdP"' `"Ybbd8"' 88 88 88`YbbdP"' "Y888 888
88
88
db 88""Yb 88""Yb db dP""b8 888888 88 dP""b8 db 88 88
dPYb 88__dP 88__dP dPYb dP `" 88 88 dP `" dPYb 88 88
dP__Yb 88""" 88"Yb dP__Yb Yb 88 88 Yb dP__Yb 88 .o
dP""""Yb 88 88 Yb dP""""Yb YboodP 88 88 YboodP dP""""Yb 88ood8
88 88b 88 888888 88""Yb dP"Yb 8888b. 88 88 dP""b8 888888 88 dP"Yb 88b 88
88 88Yb88 88 88__dP dP Yb 8I Yb 88 88 dP `" 88 88 dP Yb 88Yb88
88 88 Y88 88 88"Yb Yb dP 8I dY Y8 8P Yb 88 88 Yb dP 88 Y88
88 88 Y8 88 88 Yb YbodP 8888Y" `YbodP' YboodP 88 88 YbodP 88 Y8
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
* * * * * * * * * * * * * * * * * * * * * *
*
* This isn't very written yet.
*
* In the meantime, here are some other good resources:
*
* - http://eloquentjavascript.net/1st_edition/chapter6.html
* - http://fr.umio.us/why-ramda/
*
* * * * * * * * * * * * * * * * * * * * * *
*
* table of contents
*
* preface --- Intro, `assert`
* one ------- Function Composition
* two ------- Collections: Map + Filter + Reduce
* three ----- Loud Pipes: Ramda, Currying, and Data-Last
* four ------ Lenses
* five ------ Memoize: Managing State
* six ------- Observables: User Interaction
* seven ----- Promises, Async + Await
* eight ----- Sagas: Modeling long async processes
* nine ------ React: Functional HTML
* ten ------- Card Game: Putting It All Together
*
* * * * * * * * * * * * * * * * * * * * * *
*
* credits
*
* fonts from here:
*
* - http://patorjk.com/software/taag/#p=display&f=Univers
* - http://patorjk.com/software/taag/#p=display&f=4Max
*
* cross pattern from here:
*
* - http://chris.com/ascii/index.php?art=art%20and%20design/patterns
*/
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
ad88
d8"
88
8b,dPPYba, 8b,dPPYba, ,adPPYba, MM88MMM ,adPPYYba, ,adPPYba, ,adPPYba,
88P' "8a 88P' "Y8 a8P_____88 88 "" `Y8 a8" "" a8P_____88
88 d8 88 8PP""""""" 88 ,adPPPPP88 8b 8PP"""""""
88b, ,a8" 88 "8b, ,aa 88 88, ,88 "8a, ,aa "8b, ,aa
88`YbbdP"' 88 `"Ybbd8"' 88 `"8bbdP"Y8 `"Ybbd8"' `"Ybbd8"'
88
88
* * * * * * * * * * * * * * * * * * * * * *
*
* This is not an especially academic introduction.
*
* First, I'll define function composition.
*
* Then, I'll talk about some techniques for using
* function composition to accomplish real tasks.
*
* I hope I'll convince you that the approach can
* lead to programs that are easy to read, understand,
* and modify.
*/
/**
* TODO: add other resourecs
*/
/**
* Assert
*
* I'll be using this function throughout
* this thingy to say something is true.
*
* Rather than saying
*
* 1 + 1 // 2
*
* or
*
* 1 + 1 equals 2
*
* I'll say
*
* assert(
* 1 + 1 === 2
* )
*
* If you see 'ASSERTION FAILED!!!' in your
* browser's console, you'll know I'm lying.
*/
const assertOne = assertion => {
if (assertion !== true) {
throw Error('ASSERTION FAILED!!!')
}
}
const assertMany = (...assertions) => {
assertions.forEach(assertOne)
}
const assert = assertMany
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
,adPPYba, 8b,dPPYba, ,adPPYba,
a8" "8a 88P' `"8a a8P_____88
8b d8 88 88 8PP"""""""
"8a, ,a8" 88 88 "8b, ,aa
`"YbbdP"' 88 88 `"Ybbd8"'
Function Composition
* * * * * * * * * * * * * * * * * * * * * *
* TODO: why do I need to know this?
* - add a more real-life example
*/
// so we have two functions, F and G:
const F = x => x + 10
const G = x => x * 2
assert(
F(0) === 10,
F(1) === 11,
F(2) === 12,
F(3) === 13,
F(4) === 14
)
assert(
G(0) === 0,
G(1) === 2,
G(2) === 4,
G(3) === 6,
G(4) === 8
)
// and we can use them together
assert(
F(G(0)) === 10,
F(G(1)) === 12,
F(G(2)) === 14,
F(G(3)) === 16,
F(G(4)) === 18
)
// we can even talk about a new function,
// that applies both G and F to a value
// in succession:
const FoG = v => F(G(v))
assert(
FoG(0) === F(G(0)),
FoG(1) === F(G(1)),
FoG(2) === F(G(2)),
FoG(3) === F(G(3)),
FoG(4) === F(G(4))
)
// Taking multiple functions and creating a
// new function that applies them both like
// this is called `composing` them.
// (the o is from math, where the symbol ∘ is
// used to represent this operation)
// we can define a function for doing that:
const compose2 = (fnA, fnB) => x => fnA(fnB(x))
const _FoG = compose2(F, G)
assert(
_FoG(0) === FoG(0),
_FoG(1) === FoG(1),
_FoG(2) === FoG(2),
_FoG(3) === FoG(3),
_FoG(4) === FoG(4)
)
// Ramda has a function called compose,
// too, except it composes any number
// of functions. We can define it like
// this:
const compose = (...fns) =>
fns.reduce(
(lastFn, nextFn) => compose2(lastFn, nextFn)
)
const timesTenPlusOneSquared = compose(
x => x * x,
x => x + 1,
x => x * 10
)
assert(
timesTenPlusOneSquared(0) === 1,
timesTenPlusOneSquared(1) === 11 * 11,
timesTenPlusOneSquared(2) === 21 * 21
)
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
,d
88
MM88MMM 8b db d8 ,adPPYba,
88 `8b d88b d8' a8" "8a
88 `8b d8'`8b d8' 8b d8
88, `8bd8' `8bd8' "8a, ,a8"
"Y888 YP YP `"YbbdP"'
Collections:
Map + Filter + Reduce
* * * * * * * * * * * * * * * * * * * * * *
* TODO: "you'll use these all the time" blurb
*/
// Let's say you have an array of cities:
const cities = [
{ name: 'Brussels', population: 1175173 },
{ name: 'New York City', population: 8550405 },
{ name: 'Worcester', population: 101328 },
{ name: 'Chicago', population: 2720546 }
]
// If you want an array of city _names_, you can use map!
//
// map takes a function as its argument, and creates a
// new array by applying that function to every element
// in the original array.
const _cityNames = cities.map(city => city.name)
// ^ that would work, but let's use a
const getCityName = city => city.name
const cityNames = cities.map(getCityName)
assert(
R.equals(
cityNames,
['Brussels', 'New York City', 'Worcester', 'Chicago']
)
)
// If you want an array of the cities with population
// over a million, you can use filter!
//
// filter takes a predicate function (that is, a
// function that returns true or false), and creates
// a new array by applying your function it to every
// element, and keeping the element if it returns true.
const getCityPopulation = city => city.population
const isCityBig = city => getCityPopulation(city) > 1000000
const bigCityNames = cities.filter(isCityBig)
.map(getCityName)
assert(
R.equals(
bigCityNames,
['Brussels', 'New York City', 'Chicago']
)
)
// If you want to string about it, you can use reduce!
// reduce is for turning an aray of things into one
// other thing.
//
// It builds up that other thing by running through
// each element, and passing it, and the unfinished
// thing it's building.
//
// The second argument is an initial value for the
// built thing.
const reduceWithComma = (string, newItem) => string
? string + ', ' + newItem
: newItem
const listOfBigCityNames = cities.filter(isCityBig)
.map(getCityName)
.reduce(
reduceWithComma,
null
)
assert(
listOfBigCityNames === 'Brussels, New York City, Chicago'
)
/**
* Map
*/
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
88
,d 88
88 88
MM88MMM 88,dPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
88 88P' "8a 88P' "Y8 a8P_____88 a8P_____88
88 88 88 88 8PP""""""" 8PP"""""""
88, 88 88 88 "8b, ,aa "8b, ,aa
"Y888 88 88 88 `"Ybbd8"' `"Ybbd8"'
Loud Pipes:
Ramda, Currying and Data-Last
* * * * * * * * * * * * * * * * * * * * * *
*
* We have that really nice map/filter/reduce chain
* from the last section, but now that we aren't
* dealing with an array anymore, the chain's kinda
* over.
*
* Here we'll explore another approach to sequencing
* actions that's more versatile.
*/
/**
* We already looked at `compose`.
*
* the order in which we pass functions to
* compose is the order we'd write them:
*
* compose(F, G)(x) === F(G(x))
*
* but it's backwards: first G is applied
* to x, then F is applied to G of X.
*
* For longer lists of functions, I like to
* think of them in the opposite order, with pipe.
*
* pipe is like compose but with the functions
* passed in in the order they're applied.
*/
const pipe = (...fns) => {
fns.reverse()
return compose(...fns)
}
const __FoG = pipe(G, F)
assert(
__FoG(0) === FoG(0),
__FoG(1) === FoG(1),
__FoG(2) === FoG(2),
__FoG(3) === FoG(3),
__FoG(4) === FoG(4)
)
/**
* Map / Filter / Reduce => pipe
*
* We were looking at this chain before:
*
* .filter(isCityBig)
* .map(getCityName)
* .reduce(reduceWithComma, null)
*
* I'll model that chain as a function, using
* pipe:
*/
const getListOfBigCityNames = pipe(
cities => cities.filter(isCityBig),
cities => cities.map(getCityName),
cities => cities.reduce(reduceWithComma, null)
)
assert(
getListOfBigCityNames(cities) === listOfBigCityNames
)
/**
* That's pretty nice. We can use the same
* transformation on other lists of cities,
* _and_ (more importantly) we now have a model
* for how to add more things to the chain, now
* that we've turned the array into a string:
*/
const exclaimListOfBigCityNames = pipe(
cities => cities.filter(isCityBig),
cities => cities.map(getCityName),
cities => cities.reduce(reduceWithComma, null),
cities => `${cities}! The grandest cities in the world!`
)
assert(
exclaimListOfBigCityNames(cities) === 'Brussels, New York City, Chicago! The grandest cities in the world!'
)
/**
* Seeing the word `cities` this much irks me.
*
* What if we add support for Provinces?
*
* They have names and populations too!
*
* There's a library called Ramda that's built
* around the idea of chaining functions together
* like this.
*
* It provides implementations of map, filter, and
* reduce that don't need to be given all their
* arguments at the same time:
*/
const _exclaimListOfBigCityNames = pipe(
R.filter(isCityBig),
R.map(getCityName),
R.reduce(reduceWithComma, null),
cities => `${cities}! The grandest cities in the world!`
)
assert(
exclaimListOfBigCityNames(cities) === _exclaimListOfBigCityNames(cities)
)
/**
* I'll build up a little function army using
* Ramda, and then use it to recreate that pipe's
* transformations.
*
* Note that none of this code even uses the word 'city'
*/
const propIsLargerThan = prop => minimum => R.propSatisfies(R.gt(minimum), prop)
const populationIsLargerThan = propIsLargerThan('population')
const isLargePopulation = populationIsLargerThan(1000000)
const isSmallPopulation = R.complement(isLargePopulation)
const dropSmallPopulations = R.filter(isSmallPopulation)
const getName = R.prop('name')
const getNames = R.map(getName)
const getNamesOfLargePopulations = R.compose(getNames, dropSmallPopulations)
const intersperseCommas = R.reduce(reduceWithComma, null)
const exclaimGrandNouns = pluralNoun => R.pipe(
intersperseCommas,
list => list + `! The grandest ${pluralNoun} in the world!`
)
const exclaimGrandCities = exclaimGrandNouns('cities')
const exclaimListOfLargePopulationNames = R.compose(exclaimGrandCities, getNamesOfLargePopulations)
assert(
exclaimListOfLargePopulationNames(cities) === exclaimListOfBigCityNames(cities)
)
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
,adPPYba, ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
I8[ "" a8P_____88 `8b d8' a8P_____88 88P' `"8a
`"Y8ba, 8PP""""""" `8b d8' 8PP""""""" 88 88
aa ]8I "8b, ,aa `8b,d8' "8b, ,aa 88 88
`"YbbdP"' `"Ybbd8"' "8" `"Ybbd8"' 88 88
Lenses
* * * * * * * * * * * * * * * * * * * * * *
*
* Lenses provide a way of representing the location of
* a value within a structure.
*
* Using a lens, you can `view` the value at that location,
* `set` the value to something else, or apply a function to
* the value and set the result, `update`ing it.
*
* Put simply, a lens is a bundle containing a getter
* and a setter function.
*/
/**
* Initial State
*
* Notice how even though a person and the bank are represented
* differently, they both amount to an amount of money.
*
* Lenses answer the question "How can we implement a function,
* `transfer`, which is equally capable of dealing with people
* as with the bank?
*/
const initialState = {
people: {
ben: { money: 100 },
andrew: { money: 100 }
},
bank: {funds: 1000}
}
/**
* Transfer Function
*
* Here's how!
*
* By defining `transfer` in terms of _lenses_, rather than, say
* strings like 'ben' and 'bank', we can make it totally agnostic
* to the shape of our data.
*/
const transfer = (amount, fromLens, toLens) => state => {
// make sure there are sufficient funds
const giverFunds = R.view(fromLens, state)
if (amount > giverFunds) {
console.error('insufficient funds! not performing transfer!')
return state
}
console.log(`transferring ${amount}`)
// functions to perform the two alterations
const withdraw = R.over(fromLens, R.add(-amount))
const deposit = R.over(toLens, R.add(amount))
// compose those two functions together
const transformState = R.compose(
withdraw, deposit
)
// apply that composed function to the state
const newState = transformState(state)
return newState
}
/**
* Make the lenses
*
* With Ramda, we can easily create a lens into an object
* with the function `lensPath`.
*
* We can also create our own arbitrary lenses by passing
* a `getter` and `setter` function into the function `R.lens`
*/
const benLens = R.lensPath(['people', 'ben', 'money'])
const andrewLens = R.lensPath(['people', 'andrew', 'money'])
const bankLens = R.lensPath(['bank', 'funds'])
/**
* Log after each transfer
*/
const logFinance = n => R.tap(state => {
const bank = R.view(bankLens, state)
const ben = R.view(benLens, state)
const andrew = R.view(andrewLens, state)
console.log(`${n} :: bank: ${bank}, ben: ${ben}, andrew: ${andrew}`)
})
/**
* Set it up
*
* We'll make a pipeline (see section four)
* that transforms several transfers
*/
const performTransfers = R.pipe(
logFinance('initial state'),
transfer(100, benLens, bankLens),
logFinance(1),
transfer(100, andrewLens, bankLens),
logFinance(2),
transfer(500, bankLens, benLens),
logFinance(3),
transfer(600, bankLens, benLens),
logFinance(4),
transfer(33, bankLens, andrewLens),
logFinance(5)
)
/**
* Do it!
*
* Now we'll apply that pipeline to the
* initial state
*/
performTransfers(initialState)
/**
* * * * * * * * * * * * * * * * * * * * * *
* SIDEBAR: lens composition
*
* Lenses are functions.
*
* a fun fact about lenses (at least, Twan van Laarhoven's
* representation, which is used by Ramda), is that they're
* _composable_.
*
* Check it out:
*/
const fancyObject = {
a: {
b: {
c: {
d: {
e: {
f: {
g: 'done!'
}
}
}
}
}
}
}
const abcLens = R.lensPath(['a', 'b', 'c'])
const defgLens = R.lensPath(['d', 'e', 'f', 'g'])
// these next two lines are equivalent
const theWholeEnchiladaLens = R.compose(abcLens, defgLens)
console.log(
R.view(theWholeEnchiladaLens, fancyObject)
)
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
88 88
"" 88 ,d
88 88
,adPPYba, 88 ,adPPYb,d8 88,dPPYba, MM88MMM
a8P_____88 88 a8" `Y88 88P' "8a 88
8PP""""""" 88 8b 88 88 88 88
"8b, ,aa 88 "8a, ,d88 88 88 88,
`"Ybbd8"' 88 `"YbbdP"Y8 88 88 "Y888
aa, ,88
Memoize: Y8bbdP"
Managing State
* * * * * * * * * * * * * * * * * * * * * *
*/
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
8b,dPPYba, 88 8b,dPPYba, ,adPPYba,
88P' `"8a 88 88P' `"8a a8P_____88
88 88 88 88 88 8PP"""""""
88 88 88 88 88 "8b, ,aa
88 88 88 88 88 `"Ybbd8"'
Observables:
User Interaction
* * * * * * * * * * * * * * * * * * * * * *
*/
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
,d
88
MM88MMM ,adPPYba, 8b,dPPYba,
88 a8P_____88 88P' `"8a
88 8PP""""""" 88 88
88, "8b, ,aa 88 88
"Y888 `"Ybbd8"' 88 88
Promises, Async + Await
* * * * * * * * * * * * * * * * * * * * * *
*/
/** _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | | |
_|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_ _|_
| | | | | | | | | | | | |
88
88
88
,adPPYba, 88 ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
a8P_____88 88 a8P_____88 `8b d8' a8P_____88 88P' `"8a
8PP""""""" 88 8PP""""""" `8b d8' 8PP""""""" 88 88
"8b, ,aa 88 "8b, ,aa `8b,d8' "8b, ,aa 88 88
`"Ybbd8"' 88 `"Ybbd8"' "8" `"Ybbd8"' 88 88
Sagas:
Modeling long async processes
* * * * * * * * * * * * * * * * * * * * * *
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment