/** * Returns an effect that, if evaluated, will return the cached result of this * effect. Cached results will expire after `timeToLive` duration. In * addition, returns an effect that can be used to invalidate the current * cached value before the `timeToLive` duration expires. * * @tsplus static effect/core/io/Effect.Aspects cachedInvalidate * @tsplus pipeable effect/core/io/Effect cachedInvalidate */ export function cachedInvalidate(timeToLive: Duration) { return ( self: Effect ): Effect, Effect]> => Do(($) => { const environment = $(Effect.environment()) const cache = $(Ref.Synchronized.make]>>(Maybe.none)) return [ get(self, timeToLive, cache).provideEnvironment(environment), invalidate(cache) ] as const }) } function compute( self: Effect, timeToLive: Duration, start: number ): Effect]>> { return Do(($) => { const deferred = $(Deferred.make()) $(self.intoDeferred(deferred)) return Maybe.some([start + timeToLive.millis, deferred] as const) }) } function get( self: Effect, timeToLive: Duration, cache: Ref.Synchronized]>> ): Effect { return Effect.uninterruptibleMask(({ restore }) => Clock.currentTime.flatMap((time) => cache .updateSomeAndGetEffect((_) => _.fold( () => Maybe.some(compute(self, timeToLive, time)), ([end]) => end - time <= 0 ? Maybe.some(compute(self, timeToLive, time)) : Maybe.none ) ) .flatMap((a) => a._tag === "None" ? Effect.dieSync("Bug") : restore(a.value[1].await)) ) ) } function invalidate( cache: Ref.Synchronized]>> ): Effect { return cache.set(Maybe.none) }