Experiment to reproduce Erlang style processes in browser. The api follows the one from Erlang. All are found on the ProcessSystem class
One example is an implementation of a GenServer. The other example is 2 processes talking to each other.
-
First, import the ProcessSystem create a new instance of one.
const ProcessSystem = require("erlang-processes"); let system = new ProcessSystem();
-
Now you can spawn processes using the system.
A process will switch to other processes when yield is used and will run until it completes.
var pid1 = system.spawn(function*(){ while(true){ yield system.receive(function(value){ return console.log(value); }); system.send(pid2, "message from 1"); } }); system.register("Sally", pid1); var pid2 = system.spawn(function*(){ while(true){ system.send("Sally", "message from 2"); yield system.receive(function(value){ return console.log(value); }); } });
-
ProcessSystem
spawn(fun*) : pid- Starts a process represented by the given generator functionspawn(module, fun, args) : pid- Starts a process using the generator function from the specified modulelink(pid) : void- links the current process with the process from the given pidunlink(pid) : void- unlinks the current process from the process from the given pidregister(name, pid) : void- registers the given name to the pidregistered(name) : pid- returns the pid registered by the given name or null if not registeredunregister(pid) : void- unregisters the names associated with the pidpid(): pid` - returns the current process's pidpidof(obj) : pid- takes the input and tries to find the pid. Input can be apid,Process, or name the pid is associated withsend(pid, msg) : msg- sends a message the the process represented by the pidreceive(fun, timeout = 0, timeoutFn = () => true)- Tells the current process to receive a message that the function can handle. If no match then the process is put in the suspended state until a message arrives or the timeout is reached. If the timeout is reached and no msg matches, then the timeoutFn is calledsleep(duration)- puts the current process to sleepexit(reason)- terminates the current process with the given reason.exit(pid, reason)- tells the process with the pid to exit with the given reasonerror(reason)- terminates the current process with an errorprocess_flag(flag, value)- Sets flags on the current process.- Note: the only flag respected is the
Symbol.for("trap_exit")flag. If value istrue, then exit signals from linked processes are turned into messages and sent to the current processes mailbox. If value isfalse, the exit is treated as normal and terminates the process. Setting it totrueis useful for supervising processes.
- Note: the only flag respected is the
put(key, value)- Adds a value to the current process's dictionaryget(key)- Gets a value from the current process's dictionaryget()- Gets the current process's dictionaryget_keys()- Gets all the keys from the current process's dictionaryerase(key)- Removes the key and the associated value from the current process`s dictionaryerase()- Removes all entries from the current process's dictionary
-
ProcessSystem.run(fun, args, context = null)- A static generator function used to wrap a normal function or generator. If fun is a function, it returns the value, if it's a generator, then it delegates yielding to the generator. -
GenServer
start(module, args)- Starts a GenServer with the given module and argsstart_link(module, args)- Starts a GenServer with the given module and argscall* (server, action)- Sends the GenServer a action and waits for it to respond with a value.cast* (server, action)- Sends the GenServer a action to update a value.stop (server)- Stops the GenServer.- Note: Genserver expects a module the has the following functions:
init(args)- Must return an array containing a symbol and the initial statehandle_call(action, from, state)- Called whenGenServer.callis called. This function is given the action, the pid of the calling process, and the current state. Must return[reply, return_value, new_state]where reply is a symbol ,usually `Symbol.for("reply"), the value to return to the process, and lastly, the new state of the GenServer.handle_cast(action, state)- Called whenGenServer.castis called. his function is given the action, and the current state. Must return[reply, return_value, new_state]where reply is a symbol ,usuallySymbol.for("noreply"), and lastly, the new state of the GenServer.
An example of a Stack using a GenServer
const ProcessSystem = require("erlang-processes");
self.system = self.system || new ProcessSystem();
const Stack = {
init: function(args){
return [Symbol.for("ok"), args];
},
handle_call: function(action, pid, state){
return [Symbol.for("reply"), state[0], state.slice(1)];
},
handle_cast: function(action, state){
return [Symbol.for("noreply"), [action[1]].concat(state)];
}
}
self.system.spawn(function*(){
const [ok, pid] = yield* ProcessSystem.run(GenServer.start, [Stack, ["hello"]]);
let a = yield* ProcessSystem.run(GenServer.call, [pid, "pop"]);
console.log(a); // "hello"
let b = yield* ProcessSystem.run(GenServer.cast, [pid, ["push", "world"]]);
console.log(b); // Symbol.for("ok")
let c = yield* ProcessSystem.run(GenServer.call, [pid, "pop"]);
console.log(c); // "world"
});