Skip to content

Instantly share code, notes, and snippets.

@christianhg
Last active November 19, 2017 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christianhg/cd449b074e77442c57effc367bc45da3 to your computer and use it in GitHub Desktop.
Save christianhg/cd449b074e77442c57effc367bc45da3 to your computer and use it in GitHub Desktop.
Caching Promises
/*
Service used to mimic a slow computation:
*/
class PersonService {
getPersons(caller) {
console.log(`> PersonService#getPersons() triggered by ${caller}`)
return new Promise(resolve =>
setTimeout(
() =>
resolve([
{
name: 'Bob',
age: 25,
gender: 'male'
},
{
name: 'Alice',
age: 30,
gender: 'female'
},
{
name: 'Charlie',
age: 17,
gender: 'male'
}
]),
3000
)
)
}
}
/*
An improperly cached service that uses the PersonService:
*/
class AgeService {
constructor(personService) {
this.personService = personService
}
getMaleMedianAge(caller) {
console.log(`> AgeService#getMaleMedianAge() triggered by ${caller}`)
if (this.cachedMaleMedianAge) {
console.log(
`> AgeService#getMaleMedianAge() returning cached result to ${caller}`
)
return Promise.resolve(this.cachedMaleMedianAge)
} else {
return this.personService
.getPersons(caller)
.then(persons => persons.filter(person => person.gender === 'male'))
.then(
males =>
males.reduce((totalAge, person) => totalAge + person.age, 0) /
males.length
)
.then(maleMedianAge => {
this.cachedMaleMedianAge = maleMedianAge
console.log(
`> AgeService#getMaleMedianAge() returning non-cached result to ${caller}`
)
return maleMedianAge
})
}
}
}
const ageService = new AgeService(new PersonService())
console.log(`
------------------------------------------------------------
Improper caching resulting in an unpredictable control flow:
------------------------------------------------------------
`)
ageService.getMaleMedianAge('sync A')
ageService.getMaleMedianAge('sync B')
ageService.getMaleMedianAge('sync C')
setTimeout(() => ageService.getMaleMedianAge('async A'), 1000)
setTimeout(() => ageService.getMaleMedianAge('async B'), 2000)
setTimeout(() => ageService.getMaleMedianAge('async C'), 3000)
setTimeout(() => ageService.getMaleMedianAge('async D'), 4000)
setTimeout(() => ageService.getMaleMedianAge('async E'), 5000)
/*
> AgeService#getMaleMedianAge() triggered by sync A
> PersonService#getPersons() triggered by sync A
> AgeService#getMaleMedianAge() triggered by sync B
> PersonService#getPersons() triggered by sync B
> AgeService#getMaleMedianAge() triggered by sync C
> PersonService#getPersons() triggered by sync C
> AgeService#getMaleMedianAge() triggered by async A
> PersonService#getPersons() triggered by async A
> AgeService#getMaleMedianAge() triggered by async B
> PersonService#getPersons() triggered by async B
> AgeService#getMaleMedianAge() triggered by async C
> PersonService#getPersons() triggered by async C
> AgeService#getMaleMedianAge() returning non-cached result to sync A
> AgeService#getMaleMedianAge() returning non-cached result to sync B
> AgeService#getMaleMedianAge() returning non-cached result to sync C
> AgeService#getMaleMedianAge() triggered by async D
> AgeService#getMaleMedianAge() returning cached result to async D
> AgeService#getMaleMedianAge() returning non-cached result to async A
> AgeService#getMaleMedianAge() triggered by async E
> AgeService#getMaleMedianAge() returning cached result to async E
> AgeService#getMaleMedianAge() returning non-cached result to async B
> AgeService#getMaleMedianAge() returning non-cached result to async C
*/
/*
A properly cached service that uses the PersonService:
*/
class BetterAgeService {
constructor(personService) {
this.personService = personService
}
getMaleMedianAge(caller) {
console.log(`> BetterAgeService#getMaleMedianAge() triggered by ${caller}`)
if (this.cachedMaleMedianAge) {
console.log(
`> BetterAgeService#getMaleMedianAge() returning cached result to ${caller}`
)
return this.cachedMaleMedianAge
} else {
this.cachedMaleMedianAge = this.personService
.getPersons(caller)
.then(persons => persons.filter(person => person.gender === 'male'))
.then(
males =>
males.reduce((totalAge, person) => totalAge + person.age, 0) /
males.length
)
console.log(
`> BetterAgeService#getMaleMedianAge() returning non-cached result to ${caller}`
)
return this.cachedMaleMedianAge
}
}
}
const betterAgeService = new BetterAgeService(new PersonService())
setTimeout(() => {
console.log(`
------------------------------------------------------------
Proper caching resulting in a predictable control flow:
------------------------------------------------------------
`)
betterAgeService.getMaleMedianAge('sync A')
betterAgeService.getMaleMedianAge('sync B')
betterAgeService.getMaleMedianAge('sync C')
setTimeout(() => betterAgeService.getMaleMedianAge('async A'), 1000)
setTimeout(() => betterAgeService.getMaleMedianAge('async B'), 2000)
setTimeout(() => betterAgeService.getMaleMedianAge('async C'), 3000)
setTimeout(() => betterAgeService.getMaleMedianAge('async D'), 4000)
setTimeout(() => betterAgeService.getMaleMedianAge('async E'), 5000)
}, 7000)
/*
> BetterAgeService#getMaleMedianAge() triggered by sync A
> PersonService#getPersons() triggered by sync A
> BetterAgeService#getMaleMedianAge() returning non-cached result to sync A
> BetterAgeService#getMaleMedianAge() triggered by sync B
> BetterAgeService#getMaleMedianAge() returning cached result to sync B
> BetterAgeService#getMaleMedianAge() triggered by sync C
> BetterAgeService#getMaleMedianAge() returning cached result to sync C
> BetterAgeService#getMaleMedianAge() triggered by async A
> BetterAgeService#getMaleMedianAge() returning cached result to async A
> BetterAgeService#getMaleMedianAge() triggered by async B
> BetterAgeService#getMaleMedianAge() returning cached result to async B
> BetterAgeService#getMaleMedianAge() triggered by async C
> BetterAgeService#getMaleMedianAge() returning cached result to async C
> BetterAgeService#getMaleMedianAge() triggered by async D
> BetterAgeService#getMaleMedianAge() returning cached result to async D
> BetterAgeService#getMaleMedianAge() triggered by async E
> BetterAgeService#getMaleMedianAge() returning cached result to async E
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment