diff --git a/@nact/core/actor.test.ts b/@nact/core/actor.test.ts index ed18cb4..b5463b2 100644 --- a/@nact/core/actor.test.ts +++ b/@nact/core/actor.test.ts @@ -164,6 +164,56 @@ describe('Actor', function () { handled.should.be.true; }); + it('correctly handles an asynchronous initial state function', async function () { + let actor = spawn( + system, + function (state, msg) { + if (msg.type === 'query') { + dispatch(msg.sender, state); + return state; + } else if (msg.type === 'append') { + return state + msg.payload; + } + }, + { + name: 'Nact', + initialStateFunc: async () => { + await new Promise(f => setTimeout(f, 20)); + return 'Cheese!'; + }, + } + ); + + dispatch(actor, { payload: ' Magical wheels of golden hue!', type: 'append' }); + let result = await query(actor, x => ({ type: 'query', sender: x }), 30); + result.should.equal('Cheese! Magical wheels of golden hue!'); + }); + + it('correctly handles an async initial state function which throws an error', async function () { + let handled = false; + let actor = spawn( + system, + function (state, msg) { + if (msg.type === 'query') { + dispatch(msg.sender, state); + return state; + } else if (msg.type === 'append') { + return state + msg.payload; + } + }, + { + name: 'Nact', + initialStateFunc: async () => { + await new Promise(f => setTimeout(f, 15)); + throw new Error('A bad moon is on the rise'); + }, + onCrash: (_: any, __: any, ctx: { stop: any; }) => { handled = true; return ctx.stop; } + } + ); + await retry(() => isStopped(actor).should.be.true, 12, 10); + handled.should.be.true; + }); + it('evalutes in order when returning a promise from a stateful actor function', async function () { let child = spawn( system, diff --git a/@nact/core/actor.ts b/@nact/core/actor.ts index c2a82ce..8340827 100644 --- a/@nact/core/actor.ts +++ b/@nact/core/actor.ts @@ -56,7 +56,8 @@ export class Actor | ((msg: any, err: any, ctx: any, child?: undefined | LocalActorRef) => any); initialState: State | undefined; - initialStateFunc: ((ctx: ActorContext) => State) | undefined; + initialStateFunc: ((ctx: ActorContext) => State | Promise) | undefined; + initializeStatePromise: Promise shutdownPeriod?: Milliseconds; state: any; timeout?: Milliseconds; @@ -101,16 +102,20 @@ export class Actor x.stop()); - this.initializeState(); + this.initializeStatePromise = this.initializeState(); this.resume(); } @@ -287,6 +292,7 @@ export class Actor { try { + await this.waitUntilInitialized(); let ctx = this.createContext(); let next = await Promise.resolve(this.f.call(ctx, this.state, message, ctx)); this.state = next; @@ -350,7 +356,7 @@ export type ActorProps, initialState?: State, - initialStateFunc?: (ctx: ActorContext) => State, + initialStateFunc?: (ctx: ActorContext) => State | Promise, afterStop?: (state: State, ctx: ActorContext) => void | Promise };