-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
e7ad928
commit 2343eaf
Showing
2 changed files
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import {LinkedList} from 'dbl-linked-list-ds' | ||
import {CB} from '../internals/CB' | ||
import {FIO, IO, UIO} from './FIO' | ||
|
||
export class Await<E, A> { | ||
private readonly Q = new LinkedList<[CB<E>, CB<A>]>() | ||
private result: A | undefined | ||
private flag = false | ||
private resolved = false | ||
|
||
public static of<E = never, A = never>(): UIO<Await<E, A>> { | ||
return FIO.uio(() => new Await()) | ||
} | ||
|
||
public set(io: IO<E, A>): UIO<boolean> { | ||
return this.isSet().chain(flag => | ||
flag | ||
? FIO.of(false) | ||
: this.setFlag(true) | ||
.and(io.chain(result => this.update(result))) | ||
.const(true) | ||
) | ||
} | ||
|
||
public isSet(): UIO<boolean> { | ||
return FIO.uio(() => this.flag) | ||
} | ||
|
||
public get(): IO<E, A | undefined> { | ||
return this.isResolved().chain(resolved => | ||
resolved ? FIO.of(this.result) : this.addListener() | ||
) | ||
} | ||
|
||
private addListener(): FIO<unknown, E, A> { | ||
return FIO.asyncIO((rej, res) => { | ||
const id = this.Q.add([rej, res]) | ||
|
||
return {cancel: () => this.Q.remove(id)} | ||
}) | ||
} | ||
|
||
private setFlag(value: boolean): UIO<void> { | ||
return FIO.uio(() => void (this.flag = value)) | ||
} | ||
|
||
private isResolved(): UIO<boolean> { | ||
return FIO.uio(() => this.resolved) | ||
} | ||
|
||
private update(result: A): UIO<void> { | ||
return FIO.uio(() => { | ||
this.result = result | ||
this.resolved = true | ||
if (this.result !== undefined) { | ||
while (this.Q.length > 0) { | ||
const cb = this.Q.shift() as [CB<E>, CB<A>] | ||
cb[1](this.result) | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import {assert} from 'chai' | ||
import {Await} from '../src/main/Await' | ||
import {FIO} from '../src/main/FIO' | ||
import {testRuntime} from '../src/runtimes/TestRuntime' | ||
|
||
describe('Await', () => { | ||
describe('of', () => { | ||
it('should create an instance of Await', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync(Await.of()) | ||
assert.instanceOf(actual, Await) | ||
}) | ||
}) | ||
|
||
describe('set', () => { | ||
it('should return true', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, string>().chain(await => await.set(FIO.of('Hi'))) | ||
) | ||
assert.ok(actual) | ||
}) | ||
}) | ||
|
||
describe('set', () => { | ||
it('should return true', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, string>().chain(await => await.set(FIO.of('Hi'))) | ||
) | ||
assert.ok(actual) | ||
}) | ||
it('should set only once', () => { | ||
const runtime = testRuntime({color: 'red'}) | ||
const actual = runtime.executeSync( | ||
Await.of<never, string>().chain(await => | ||
await | ||
.set(FIO.of('Hi')) | ||
.and(await.set(FIO.of('Bye'))) | ||
.and(await.get()) | ||
) | ||
) | ||
assert.strictEqual(actual, 'Hi') | ||
}) | ||
|
||
it('should return false if its not set', () => { | ||
const runtime = testRuntime({color: 'red'}) | ||
const actual = runtime.executeSync( | ||
Await.of<never, string>().chain(await => | ||
await.set(FIO.of('Hi')).and(await.set(FIO.of('Bye'))) | ||
) | ||
) | ||
assert.notOk(actual) | ||
}) | ||
}) | ||
|
||
describe('get', () => { | ||
it('should return the IO value', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, string>().chain(await => | ||
await.set(FIO.of('Hi')).and(await.get()) | ||
) | ||
) | ||
assert.strictEqual(actual, 'Hi') | ||
}) | ||
|
||
it('should not resolve unless set', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, string>().chain(await => await.get()) | ||
) | ||
assert.isUndefined(actual) | ||
}) | ||
|
||
it('should not return', () => { | ||
const runtime = testRuntime({color: 'red'}) | ||
const await = runtime.executeSync(Await.of<never, string>()) as Await< | ||
never, | ||
string | ||
> | ||
let actual: string | undefined | ||
runtime.execute(await.get(), r => (actual = r)) | ||
runtime.execute(await.set(FIO.timeout('Hey', 1000))) | ||
|
||
assert.isUndefined(actual) | ||
runtime.scheduler.run() | ||
assert.strictEqual(actual, 'Hey') | ||
}) | ||
}) | ||
|
||
describe('isSet()', () => { | ||
it('should return false initially', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, string>().chain(await => await.isSet()) | ||
) | ||
|
||
assert.notOk(actual) | ||
}) | ||
|
||
it('should return true after setting', () => { | ||
const actual = testRuntime({color: 'red'}).executeSync( | ||
Await.of<never, number>().chain(await => | ||
await.set(FIO.of(100)).and(await.isSet()) | ||
) | ||
) | ||
|
||
assert.ok(actual) | ||
}) | ||
}) | ||
}) |