Skip to content

Commit

Permalink
Merge pull request #129 from AceFire6/jethro/change/initial-state-fun…
Browse files Browse the repository at this point in the history
…c-async

feat(actor): allow async initialStateFunc
  • Loading branch information
ncthbrt authored Dec 12, 2022
2 parents ef0629a + 5f9f9a1 commit 045360a
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 7 deletions.
50 changes: 50 additions & 0 deletions @nact/core/actor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
20 changes: 13 additions & 7 deletions @nact/core/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export class Actor<State, Msg, ParentRef extends LocalActorSystemRef | LocalActo
immediate: number | undefined;
onCrash: SupervisionActorFunc<Msg, ParentRef> | ((msg: any, err: any, ctx: any, child?: undefined | LocalActorRef<unknown>) => any);
initialState: State | undefined;
initialStateFunc: ((ctx: ActorContext<Msg, ParentRef>) => State) | undefined;
initialStateFunc: ((ctx: ActorContext<Msg, ParentRef>) => State | Promise<State>) | undefined;
initializeStatePromise: Promise<void>
shutdownPeriod?: Milliseconds;
state: any;
timeout?: Milliseconds;
Expand Down Expand Up @@ -101,16 +102,20 @@ export class Actor<State, Msg, ParentRef extends LocalActorSystemRef | LocalActo
} else {
this.setTimeout = unit;
}
this.initializeState();
this.initializeStatePromise = this.initializeState();
this.setTimeout();
}

initializeState() {
async waitUntilInitialized() {
await this.initializeStatePromise;
}

async initializeState() {
if (this.initialStateFunc) {
try {
this.state = this.initialStateFunc(this.createContext());
this.state = await Promise.resolve(this.initialStateFunc(this.createContext()));
} catch (e) {
this.handleFault(undefined, e as Error | undefined);
await this.handleFault(undefined, e as Error | undefined);
}
} else {
this.state = this.initialState;
Expand All @@ -120,7 +125,7 @@ export class Actor<State, Msg, ParentRef extends LocalActorSystemRef | LocalActo

reset() {
[...this.children.values()].forEach(x => x.stop());
this.initializeState();
this.initializeStatePromise = this.initializeState();
this.resume();
}

Expand Down Expand Up @@ -287,6 +292,7 @@ export class Actor<State, Msg, ParentRef extends LocalActorSystemRef | LocalActo
this.busy = true;
this.immediate = addMacrotask(async () => {
try {
await this.waitUntilInitialized();
let ctx = this.createContext();
let next = await Promise.resolve(this.f.call(ctx, this.state, message, ctx));
this.state = next;
Expand Down Expand Up @@ -350,7 +356,7 @@ export type ActorProps<State, Msg, ParentRef extends ActorSystemRef | LocalActor
shutdownAfter?: Milliseconds,
onCrash?: SupervisionActorFunc<Msg, ParentRef>,
initialState?: State,
initialStateFunc?: (ctx: ActorContext<Msg, ParentRef>) => State,
initialStateFunc?: (ctx: ActorContext<Msg, ParentRef>) => State | Promise<State>,
afterStop?: (state: State, ctx: ActorContext<Msg, ParentRef>) => void | Promise<void>
};

Expand Down

0 comments on commit 045360a

Please sign in to comment.