Skip to content

Commit 2343eaf

Browse files
committed
feat(await): add await class
1 parent e7ad928 commit 2343eaf

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

src/main/Await.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {LinkedList} from 'dbl-linked-list-ds'
2+
import {CB} from '../internals/CB'
3+
import {FIO, IO, UIO} from './FIO'
4+
5+
export class Await<E, A> {
6+
private readonly Q = new LinkedList<[CB<E>, CB<A>]>()
7+
private result: A | undefined
8+
private flag = false
9+
private resolved = false
10+
11+
public static of<E = never, A = never>(): UIO<Await<E, A>> {
12+
return FIO.uio(() => new Await())
13+
}
14+
15+
public set(io: IO<E, A>): UIO<boolean> {
16+
return this.isSet().chain(flag =>
17+
flag
18+
? FIO.of(false)
19+
: this.setFlag(true)
20+
.and(io.chain(result => this.update(result)))
21+
.const(true)
22+
)
23+
}
24+
25+
public isSet(): UIO<boolean> {
26+
return FIO.uio(() => this.flag)
27+
}
28+
29+
public get(): IO<E, A | undefined> {
30+
return this.isResolved().chain(resolved =>
31+
resolved ? FIO.of(this.result) : this.addListener()
32+
)
33+
}
34+
35+
private addListener(): FIO<unknown, E, A> {
36+
return FIO.asyncIO((rej, res) => {
37+
const id = this.Q.add([rej, res])
38+
39+
return {cancel: () => this.Q.remove(id)}
40+
})
41+
}
42+
43+
private setFlag(value: boolean): UIO<void> {
44+
return FIO.uio(() => void (this.flag = value))
45+
}
46+
47+
private isResolved(): UIO<boolean> {
48+
return FIO.uio(() => this.resolved)
49+
}
50+
51+
private update(result: A): UIO<void> {
52+
return FIO.uio(() => {
53+
this.result = result
54+
this.resolved = true
55+
if (this.result !== undefined) {
56+
while (this.Q.length > 0) {
57+
const cb = this.Q.shift() as [CB<E>, CB<A>]
58+
cb[1](this.result)
59+
}
60+
}
61+
})
62+
}
63+
}

test/Await.test.ts

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import {assert} from 'chai'
2+
import {Await} from '../src/main/Await'
3+
import {FIO} from '../src/main/FIO'
4+
import {testRuntime} from '../src/runtimes/TestRuntime'
5+
6+
describe('Await', () => {
7+
describe('of', () => {
8+
it('should create an instance of Await', () => {
9+
const actual = testRuntime({color: 'red'}).executeSync(Await.of())
10+
assert.instanceOf(actual, Await)
11+
})
12+
})
13+
14+
describe('set', () => {
15+
it('should return true', () => {
16+
const actual = testRuntime({color: 'red'}).executeSync(
17+
Await.of<never, string>().chain(await => await.set(FIO.of('Hi')))
18+
)
19+
assert.ok(actual)
20+
})
21+
})
22+
23+
describe('set', () => {
24+
it('should return true', () => {
25+
const actual = testRuntime({color: 'red'}).executeSync(
26+
Await.of<never, string>().chain(await => await.set(FIO.of('Hi')))
27+
)
28+
assert.ok(actual)
29+
})
30+
it('should set only once', () => {
31+
const runtime = testRuntime({color: 'red'})
32+
const actual = runtime.executeSync(
33+
Await.of<never, string>().chain(await =>
34+
await
35+
.set(FIO.of('Hi'))
36+
.and(await.set(FIO.of('Bye')))
37+
.and(await.get())
38+
)
39+
)
40+
assert.strictEqual(actual, 'Hi')
41+
})
42+
43+
it('should return false if its not set', () => {
44+
const runtime = testRuntime({color: 'red'})
45+
const actual = runtime.executeSync(
46+
Await.of<never, string>().chain(await =>
47+
await.set(FIO.of('Hi')).and(await.set(FIO.of('Bye')))
48+
)
49+
)
50+
assert.notOk(actual)
51+
})
52+
})
53+
54+
describe('get', () => {
55+
it('should return the IO value', () => {
56+
const actual = testRuntime({color: 'red'}).executeSync(
57+
Await.of<never, string>().chain(await =>
58+
await.set(FIO.of('Hi')).and(await.get())
59+
)
60+
)
61+
assert.strictEqual(actual, 'Hi')
62+
})
63+
64+
it('should not resolve unless set', () => {
65+
const actual = testRuntime({color: 'red'}).executeSync(
66+
Await.of<never, string>().chain(await => await.get())
67+
)
68+
assert.isUndefined(actual)
69+
})
70+
71+
it('should not return', () => {
72+
const runtime = testRuntime({color: 'red'})
73+
const await = runtime.executeSync(Await.of<never, string>()) as Await<
74+
never,
75+
string
76+
>
77+
let actual: string | undefined
78+
runtime.execute(await.get(), r => (actual = r))
79+
runtime.execute(await.set(FIO.timeout('Hey', 1000)))
80+
81+
assert.isUndefined(actual)
82+
runtime.scheduler.run()
83+
assert.strictEqual(actual, 'Hey')
84+
})
85+
})
86+
87+
describe('isSet()', () => {
88+
it('should return false initially', () => {
89+
const actual = testRuntime({color: 'red'}).executeSync(
90+
Await.of<never, string>().chain(await => await.isSet())
91+
)
92+
93+
assert.notOk(actual)
94+
})
95+
96+
it('should return true after setting', () => {
97+
const actual = testRuntime({color: 'red'}).executeSync(
98+
Await.of<never, number>().chain(await =>
99+
await.set(FIO.of(100)).and(await.isSet())
100+
)
101+
)
102+
103+
assert.ok(actual)
104+
})
105+
})
106+
})

0 commit comments

Comments
 (0)