-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add toPromise * Typecheck * Just need to fix types * Revert "Just need to fix types" This reverts commit 31a24c5. * Fix types * Fix tests * Refactor test * Add jsdoc * Tweak things * Add test to handle errors * Add tests and immediate resolve/rejection cases * tweak things * remove redundant code * Add changeset --------- Co-authored-by: Mateusz Burzyński <[email protected]>
- Loading branch information
1 parent
c111273
commit ca58904
Showing
5 changed files
with
216 additions
and
15 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,26 @@ | ||
--- | ||
'xstate': minor | ||
--- | ||
|
||
Introduce `toPromise(actor)`, which creates a promise from an `actor` that resolves with the actor snapshot's `output` when done, or rejects with the actor snapshot's `error` when it fails. | ||
|
||
```ts | ||
import { createMachine, createActor, toPromise } from 'xstate'; | ||
|
||
const machine = createMachine({ | ||
// ... | ||
states: { | ||
// ... | ||
done: { type: 'final', output: 42 } | ||
} | ||
}); | ||
|
||
const actor = createActor(machine); | ||
|
||
actor.start(); | ||
|
||
const output = await toPromise(actor); | ||
|
||
console.log(output); | ||
// => 42 | ||
``` |
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
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,36 @@ | ||
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>> { | ||
return new Promise((resolve, reject) => { | ||
actor.subscribe({ | ||
complete: () => { | ||
resolve(actor.getSnapshot().output); | ||
}, | ||
error: reject | ||
}); | ||
}); | ||
} |
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
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,137 @@ | ||
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 () => { | ||
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')); | ||
|
||
await expect(toPromise(actor)).rejects.toEqual(new Error('oh noes')); | ||
}); | ||
}); |