Skip to content

Latest commit

 

History

History
126 lines (120 loc) · 4.33 KB

10. Concurrent Structures: Promise - Work Synchronization.md

File metadata and controls

126 lines (120 loc) · 4.33 KB

10. Concurrent Structures: Promise - Work Synchronization

// Deferred.ts
declare interface Deferred<in out A, in out E = never> {}

declare const make: <A, E = never>() => Effect<Deferred<A, E>>
declare const await: <A, E>(self: Deferred<A, E>) => Effect<A, E>
declare const fail: <A, E>(self: Deferred<A, E>, error: E): Effect<boolean>
declare const succeed: <A, E>(self: Deferred<A, E>, value: A): Effect<boolean>
// https://effect.website/play#52b365d3d37b
const program = Effect.gen(function*() {
  const deferred = yield* Deferred.make<void>()
  const hello = yield* Effect.log("Hello, ").pipe(
    Effect.zipRight(Deferred.succeed(deferred, constVoid())),
    Effect.fork
  )
  const world = yield* Deferred.await(deferred).pipe(
    Effect.zipRight(Effect.log(" World!")),
    Effect.fork
  )
  yield* Fiber.join(hello).pipe(
    Effect.zipRight(Fiber.join(world))
  )
})
// Hello, 
//  World!

10.1 Various Ways of Completing Promises

// Deferred.ts
declare const die: <A, E>(self: Deferred<A, E>, defect: unknown): Effect<boolean>
declare const done: <A, E>(self: Deferred<A, E>, exit: Exit<A, E>): Effect<boolean>
declare const failCause: <A, E>(self: Deferred<A, E>, cause: Cause.Cause<E>): Effect<boolean>
// Deferred.ts
declare const complete: <A, E>(self: Deferred<A, E>, effect: Effect<A, E>): Effect<boolean>
declare const completeWith: <A, E>(self: Deferred<A, E>, effect: Effect<A, E>): Effect<boolean>
// https://effect.website/play#f4d72120f81c
const complete = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.complete(deferred, Random.nextInt)
  const l = yield* Deferred.await(deferred)
  const r = yield* Deferred.await(deferred)
  return [l, r]
})
// [ 3842191075376426, 3842191075376426 ]
const completeWith = Effect.gen(function*() {
  const deferred = yield* Deferred.make<number>()
  yield* Deferred.completeWith(deferred, Random.nextInt)
  const l = yield* Deferred.await(deferred)
  const r = yield* Deferred.await(deferred)
  return [l, r]
})
// [ 7746174849012290, 2939158412056364 ]

10.2 Waiting on a Promise

// Deferred.ts
declare const await: <A, E>(self: Deferred<A, E>) => Effect<A, E>
declare const isDone: <A, E>(self: Deferred<A, E>) => Effect<boolean>
declare const pool: <A, E>(self: Deferred<A, E>) => Effect<Option<Effect<A, E>>>

10.3 Promises and Interruption

// Deferred.ts
declare const interrupt: <A, E>(self: Deferred<A, E>) => Effect<boolean>
declare const interruptWith: <A, E>(self: Deferred<A, E>, fiberId: FiberId): Effect<boolean>

10.4 Combining Ref and Promise for More Complicated Concurrency Scenarios

// https://effect.website/play#2779e539ebbf
interface Cache<K, E, V> {
  get(key: K): Effect<V, E>
}

const make = <K, R, E, V>(lookup: (key: K) => Effect<V, E, R>) =>
  Effect.gen(function*() {
    const requirements = yield* Effect.context<R>()
    const ref = yield* Ref.make(HashMap.empty<K, Deferred<V, E>>())

    const cache: Cache<K, E, V> = {
      get: (key: K) =>
        Deferred.make<V, E>().pipe(Effect.flatMap((deferred) => {
          return Ref.modify(ref, (map) => {
            const [either, m] = HashMap.get(map, key).pipe(Option.match({
              onSome: (value) => [Either.right(value), map] as const,
              onNone: () => [Either.left(deferred), HashMap.set(map, key, deferred)] as const
            }))
            return [either as Either<Deferred<V, E>, Deferred<V, E>>, m]
          }).pipe(
            Effect.flatMap(
              Either.match({
                onLeft: (deferred) =>
                  lookup(key).pipe(
                    Effect.provide(requirements),
                    (lookupEffect) => Deferred.complete(deferred, lookupEffect),
                    Effect.zipRight(Deferred.await(deferred))
                  ).pipe(Effect.tap(Effect.log("lookup"))),
                onRight: (deferred) => Deferred.await(deferred).pipe(Effect.tap(Effect.log("cache")))
              })
            )
          )
        }))
    }

    return cache
  })

const programm = Effect.gen(function*() {
  const cache = yield* make((key: string) => Effect.succeed(key).pipe(Effect.delay(2000)))

  const value1 = yield* cache.get("1")
  const value2 = yield* cache.get("1")
  return [value1, value2]
})
// ------------ 2 seconds delay
// lookup
// ------------ no-delay
// cache
// ['1', '1']