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']