-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
toPromise(actorRef)
#4198
toPromise(actorRef)
#4198
Changes from 17 commits
aabb79e
0c2e562
31a24c5
620f158
c9b6227
32084ab
d01b30b
2d5059f
06331a9
9125816
0ea89d5
fc0a4c7
454e397
a037b51
13c77f6
f8cb93f
b081bb9
77d57c3
55e8bf4
8c840b0
f724bee
6e35374
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { AnyActorRef, OutputFrom } from './types.ts'; | ||
|
||
/** | ||
* Returns a promise that resolves to the `output` of the actor when it is done. | ||
* | ||
* @example | ||
* ```ts | ||
* const machine = createMachine({ | ||
* // ... | ||
* output: { | ||
* count: 42 | ||
* } | ||
* }); | ||
* | ||
* const actor = createActor(machine); | ||
* | ||
* actor.start(); | ||
* | ||
* const output = await toPromise(actor); | ||
* | ||
* console.log(output); | ||
* // logs { count: 42 } | ||
* ``` | ||
*/ | ||
export function toPromise<T extends AnyActorRef>( | ||
actor: T | ||
): Promise<OutputFrom<T>> { | ||
// TODO: this is typed as `any` | ||
const currentSnapshot = actor.getSnapshot(); | ||
|
||
if (currentSnapshot.status === 'done') { | ||
return Promise.resolve(currentSnapshot.output); | ||
} | ||
davidkpiano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (currentSnapshot.status === 'error') { | ||
return Promise.reject(currentSnapshot.error); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interestingly, this one is not redundant right now. I think this is a problem and I even thought that we had some tests related to this (although maybe indirect ones). I'll take a look at this right now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that we'll be able to remove this once this lands: #4570 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Approved ✅ |
||
|
||
return new Promise((resolve, reject) => { | ||
actor.subscribe({ | ||
complete: () => { | ||
resolve(actor.getSnapshot().output); | ||
}, | ||
error: reject | ||
}); | ||
}); | ||
} |
davidkpiano marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import { createActor, createMachine, fromPromise, toPromise } from '../src'; | ||
|
||
describe('toPromise', () => { | ||
it('should be awaitable', async () => { | ||
const promiseActor = createActor( | ||
fromPromise(() => Promise.resolve(42)) | ||
).start(); | ||
|
||
const result = await toPromise(promiseActor); | ||
|
||
result satisfies number; | ||
|
||
expect(result).toEqual(42); | ||
}); | ||
|
||
it('should await actors', async () => { | ||
const machine = createMachine({ | ||
types: {} as { | ||
output: { count: 42 }; | ||
}, | ||
initial: 'pending', | ||
states: { | ||
pending: { | ||
on: { | ||
RESOLVE: 'done' | ||
} | ||
}, | ||
done: { | ||
type: 'final' | ||
} | ||
}, | ||
output: { count: 42 } | ||
}); | ||
|
||
const actor = createActor(machine).start(); | ||
|
||
setTimeout(() => { | ||
actor.send({ type: 'RESOLVE' }); | ||
}, 1); | ||
|
||
const data = await toPromise(actor); | ||
|
||
data satisfies { count: number }; | ||
|
||
expect(data).toEqual({ count: 42 }); | ||
}); | ||
|
||
it('should await already done actors', async () => { | ||
const machine = createMachine({ | ||
types: {} as { | ||
output: { count: 42 }; | ||
}, | ||
initial: 'done', | ||
states: { | ||
done: { | ||
type: 'final' | ||
} | ||
}, | ||
output: { count: 42 } | ||
}); | ||
|
||
const actor = createActor(machine).start(); | ||
|
||
const data = await toPromise(actor); | ||
|
||
data satisfies { count: number }; | ||
|
||
expect(data).toEqual({ count: 42 }); | ||
}); | ||
|
||
it('should handle errors', async () => { | ||
const machine = createMachine({ | ||
initial: 'pending', | ||
states: { | ||
pending: { | ||
on: { | ||
REJECT: { | ||
actions: () => { | ||
throw new Error('oh noes'); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
|
||
const actor = createActor(machine).start(); | ||
|
||
setTimeout(() => { | ||
actor.send({ type: 'REJECT' }); | ||
}); | ||
|
||
try { | ||
await toPromise(actor); | ||
} catch (err) { | ||
expect(err).toEqual(new Error('oh noes')); | ||
} | ||
}); | ||
|
||
it('should immediately resolve for a done actor', async () => { | ||
const machine = createMachine({ | ||
initial: 'done', | ||
states: { | ||
done: { | ||
type: 'final' | ||
} | ||
}, | ||
output: { | ||
count: 100 | ||
} | ||
}); | ||
|
||
const actor = createActor(machine).start(); | ||
|
||
expect(actor.getSnapshot().status).toBe('done'); | ||
expect(actor.getSnapshot().output).toEqual({ count: 100 }); | ||
|
||
const output = await toPromise(actor); | ||
|
||
expect(output).toEqual({ count: 100 }); | ||
}); | ||
|
||
it('should immediately reject for an actor that had an error', async () => { | ||
expect.assertions(3); | ||
const machine = createMachine({ | ||
entry: () => { | ||
throw new Error('oh noes'); | ||
} | ||
}); | ||
|
||
const actor = createActor(machine).start(); | ||
|
||
expect(actor.getSnapshot().status).toBe('error'); | ||
expect(actor.getSnapshot().error).toEqual(new Error('oh noes')); | ||
|
||
try { | ||
await toPromise(actor); | ||
} catch (err) { | ||
expect(err).toEqual(new Error('oh noes')); | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't do much about it as long as we use
T extends AnyActorRef
pattern. You unwrap here what you provided for theTSnapshot
withinActorRef
but that's justany
(since you are usingAnyActorRef
)