ðŸŠī\zansh.in\js

Play with the controls to see how the code works. All code is present in the page, but some scrollback may be necessary to grok everything.

# memoizer / caching / invalidation

2 min read

If your function or promise is slow but predictable, wrap it in a memoizer to put it behind a cache.

# compute

function computeValue() {
return thatWasQuick()
}
function computeValueSlowly() {
return thisWillTakeAWhile()
}

# cache

const cache = new Map()
function getValue() {
return cache.has('🔑')
? cache.get('🔑')
: 'Yes, we have no 🍌'
}
function setFast() {
const value = computeValue()
cache.set('🔑', value)
}
function setSlow() {
const value = computeValueSlowly()
cache.set('🔑', value)
}

# Memoize()

const getValue = Memoize(key => {
if (key === 'fast') {
return computeValue()
}
if (key === 'slow') {
return computeValueSlowly()
}
})
function Memoize(fn) {
const cache = new Map()
return x =>
cache.has(x)
? cache.get(x)
: cache.set(x, fn(x)).get(x)
}

# Memoize() + invalidate

const getValue = Memoize(
(key, invalidate) => {
setTimeout(invalidate, seconds(3))
if (key === 'fast') {
return computeValue()
}
if (key === 'slow') {
return computeValueSlowly()
}
}
)
function Memoize(fn) {
const cache = new Map()
const Invalidater = x => () => {
cache.delete(x)
console.log(`❌ "${x}" invalidated`)
}
return x =>
cache.has(x)
? cache.get(x)
: cache
.set(x, fn(x, Invalidater(x)))
.get(x)
}
function seconds(n) {
return n * 1000
}

# Memoize() + Promise

const run = () =>
fakeFetch('ðŸĶ').then(console.log)
const runMemo = () =>
fetchMemo('🐒').then(console.log)
const fetchMemo = Memoize(
(id, invalidate) =>
fakeFetch(id)
.then(res => {
setTimeout(invalidate, seconds(7))
return res
})
.catch(invalidate)
)
function fakeFetch(id) {
return new Promise(resolve =>
setTimeout(
resolve,
seconds(1),
`Hey, ${id}`
)
)
}

Google it:

ðŸŒļ closures 🌚 dependency injection 🌷 fetch ðŸŒŧ function composition 🌞 high-order functions ðŸŒđ hoisting 💐 new Map ðŸŒļ Promise 🌚 setTimeout