SPLIT INTO MULTIPLE PAGES
Promise Patterns for Happier Relationships
Multiple Dependencies
Fallback Data
- Insert a different value if a
Promise
fails.
const getData = () => Promise.reject(new Error('Connection Timed Out'))
const getCachedData = () => Promise.resolve({name: 'Brontosaurus'})
const log = (value) => console.log(value)
getData()
.catch(getCachedData) //Could get data from localStorage
.then(log)
- Can be used any time an external resource isn't reliable
Fanning
- Taking a
Promise
and independently calling multiple.then()
on it
const dinosaurs = getDinosaurs() //Returns a promise
dinosaurs
.then(cacheDinosaurs)
dinosaurs
.then(renderDinosaurList)
dinosaurs
.then(renderDinosaursCount)
Promisify a Callback
- Taking a callback and turning it into a
Promise
const getDinosaur = (name, callback) => callback(null, {name})
const getDinosaurPromise = (name) => {
return new Promise((resolve, reject) => {
getDinosaur(name, (error, data) => {
if (error) {
return reject(error)
}
resolve(data)
})
})
}
getDinosaurPromise("velociraptor")
.then(log) // -> {name: "velociraptor"}
Gate Keeper
- Wrap DOM Ready event
// This is how JQuery's $.ready() works
const domReady = () => {
const readyState = document.readyState
return readyState === "interactive" || readyState === "complete"
? Promise.resolve()
: new Promise((resolve) =>
document.addEventListener("DOMContentLoaded", resolve))
}
const roar = () => console.log("Roar!!!")
const attachRoarHandler = () =>
document.querySelector("button").addEventListener("click", roar);
domReady() // Promise only resolves when the DOM is loaded
.then(attachRoarHandler)
- Can be coupled with fanning to great effect
Caching
- Store a
Promise
into a cache instead of the values
const cache = {}
const getDinosaur = (name) =>
cache[name]
? cache[name]
: cache[name] = Promise.resolve({name})
const log = (value) => console.log(value)
getDinosaur('Stegosaurus')
.then(log)
getDinosaur('Stegosaurus')
.then(log)
console.log(getDinosaur('Stegosaurus') === getDinosaur('Stegosaurus')) //-> true
- This way, a hundred different things can request a remote resource and it will only hit it once
- It does this by storing an unfulfilled
Promise
- It does this by storing an unfulfilled
- Synchronous values don't populate the cache until the request finished causing all of the requests that come in before the request resolves will also try and hit the external resource
Throttling
- Return the same
Promise
when something is asked for until it resolves
let throttledPromise
const resetThrottle = () => throttledPromise = undefined
const getDinosaurs = () =>
throttledPromise
? throttledPromise
: throttledPromise = Promise.resolve({name}).always(resetThrottle)
const dinosaurs = getDinosaurs()
console.log(dinosaurs === getDinosaurs()) //-> true
setTimeout(() => {
//By now, the Promise has settled and is no longer pending
console.log(dinosaurs === getDinosaurs()) //-> false
})
- This keeps data fresh, but prevents parallel requests for the same thing
- All parallel requests get the same
Promise
back until it resolves and a newPromise
is made
Fastest Promise
Promise.race
offers a method to put a time-limit on how long an asynchronous task can take
let cache = '[]'
const retrieveDinosaurs = () => //Don't resolve until 5 seconds have passed
new Promise((resolve) => setTimeout(() => resolve(["Brontosaurus"]), 2000))
const getCachedDinosaurs = () => //Don't resolve until 2 seconds have passed
new Promise((resolve) => setTimeout(() => resolve(getDinosaursFromCache()), 1000))
const saveDinosaursToCache = (dinosaurs) => cache = JSON.stringify(dinosaurs)
const getDinosaursFromCache = () => JSON.parse(cache)
const getDinosaurs = () => retrieveDinosaurs().tap(saveDinosaursToCache)
const log = (value) => console.log(value)
Promise.race([getDinosaurs(), getCachedDinosaurs()])
.tap(log)
setTimeout(
() => Promise.race([getDinosaurs(), getCachedDinosaurs()])
.tap(log),
3000)
- If a connection is unreliable, take data from the cache and update the cache later
async
/await
- The future syntax of
Promise
async function getDinosaurs () {
const dinosaurs = await api.dinosaurs.get() //returns a Promise
const names = dinosaurs.map((dinosaur) => dinosaur.name)
return names
}