epicweb-dev / cachified

🤑 wrap virtually everything that can store by key to act as cache with ttl/max-age, stale-while-validate, parallel fetch protection and type-safety support
MIT License
916 stars 26 forks source link

Let getFreshValue know whether the update is happening in the background or not #24

Closed kentcdodds closed 1 year ago

kentcdodds commented 1 year ago

Here's what I've got:


function abortTimeoutSignal(timeMs: number) {
  const abortController = new AbortController()
  void new Promise(resolve => setTimeout(resolve, timeMs)).then(() => {
    abortController.abort()
  })
  return abortController.signal
}

async function gravatarExistsForEmail({
  email,
  request,
  timings,
  forceFresh,
}: {
  email: string
  request: Request
  timings?: Timings
  forceFresh?: boolean
}) {
  return cachified({
    key: `gravatar-exists-for:${email}`,
    cache: lruCache,
    request,
    timings,
    forceFresh,
    ttl: 1000 * 20,
    staleWhileRevalidate: 1000 * 60 * 60 * 24 * 365,
    checkValue: prevValue => typeof prevValue === 'boolean',
    getFreshValue: async () => {
      const gravatarUrl = getAvatar(email, {fallback: '404'})
      try {
        const avatarResponse = await fetch(gravatarUrl, {
          method: 'HEAD',
          signal: abortTimeoutSignal(1000 * 2),
        })
        return avatarResponse.status === 200
      } catch (error: unknown) {
        console.error(`Error getting gravatar for ${email}:`, error)
        return false
      }
    },
  })
}

I have the abortTimeoutSignal thing in place so gravatar won't cause me issues if my page is waiting on it. I think if my page is waiting on it I'd prefer that the timeout time be more like 500ms, but if the update is happening in the background (SWR) then I'm fine with it taking even 10 seconds. Thoughts? Maybe an argument passed to getFreshValue?

Xiphe commented 1 year ago

Hi Kent 👋 Sounds useful to me 👍

To be clear: When you say "In the background", you mean a case where the cachified call returned a stale cache value and getFreshValue is called in the background to eventually provide a fresh value for the next call right?

kentcdodds commented 1 year ago

Exactly 👍

Xiphe commented 1 year ago

Could you give cachified@3.1.0-context-info.1 a try?

The info is passed on the new GetFreshValueConext

cachified({
  /* ... */   
  ttl: 5,
  staleWhileRevalidate: 20,
  getFreshValue({ background }) {
    console.log(background);
    return 'value'
  }
});
kentcdodds commented 1 year ago

Sweet! I'll give this a try soon and report back. Thanks!

kentcdodds commented 1 year ago

Works great 👍 Thank you.

Xiphe commented 1 year ago

Glad to hear that! It's released under v3.1.0

kentcdodds commented 1 year ago

Super, thanks a lot :)