You in fact can, but doesn’t mean you should, as pointed out as an example, here’s an async example (contrived), utilizing the push button example from the docs:
-module(pushbutton).
-behaviour(gen_statem).
-export([start/0,push/0,get_count/0,stop/0]).
-export([terminate/3,code_change/4,init/1,callback_mode/0]).
-export([on/3,off/3]).
name() -> pushbutton_statem. % The registered server name
%% API. This example uses a registered name name()
%% and does not link to the caller.
start() ->
gen_statem:start({local,name()}, ?MODULE, [], []).
push() ->
gen_statem:call(name(), push).
get_count() ->
gen_statem:call(name(), get_count).
stop() ->
gen_statem:stop(name()).
%% Mandatory callback functions
terminate(_Reason, _State, _Data) ->
void.
code_change(_Vsn, State, Data, _Extra) ->
{ok,State,Data}.
init([]) ->
%% Set the initial state + data. Data is used only as a counter.
State = off, Data = 0,
{ok,State,Data}.
callback_mode() -> state_functions.
%%% state callback(s)
off({call,From}, push, Data) ->
%% Go to 'on', increment count and reply
%% that the resulting status is 'on'
{next_state,on,Data+1,[{reply,From,on}]};
off(EventType, EventContent, Data) ->
handle_event(EventType, EventContent, Data).
on({call,From}, push, Data) ->
%% Go to 'off' and reply that the resulting status is 'off'
{next_state,off,Data,[{reply,From,off}]};
on(EventType, EventContent, Data) ->
handle_event(EventType, EventContent, Data).
%% Handle events common to all states
handle_event({call, From}, get_count, Data) ->
erlang:whereis(ping_pong) ! {self(), ping, From},
{keep_state,Data};
handle_event(info, {pong, OrigFrom}, Data) ->
gen_statem:reply(OrigFrom, Data),
{keep_state, Data};
handle_event(_, _, Data) ->
%% Ignore all other events
{keep_state, Data}.
This is a bad example, but demonstrates a thing none the less. Particularly bad because we’re relying on passing the client pid to the ping_pong
process and getting it back later so we can reply to the client, but yeah, demonstrates a thing.
Likewise, you can do the same thing but spawn a proc you pass the client pid to, does a send/recv, then signals back to your gen_statem using send again.