Table of Contents
- Async
- async [ident] { ... } [kill { ... }] [suspend { ... }] [resume { ... }]
- Example
- Kill, suspend, and resume
- Async JavaScript API
Async
Hiphop async
forms enables HipHop to control (i.e., abort, suspend,
resume, and react to) long lasting background JavaScript actions. They
are the essential ingredient for mixing undeterministic asynchronous
computations and deterministic synchronous computations. In other
words, the async
form enables well-behaving synchronous to regulate
unsteady asynchronous computations.
async [ident] { ... } [kill { ... }] [suspend { ... }] [resume { ... }]
The async
form executes a JavaScript statement and upon its completion, the
asynchronous block can resume the synchronous machine by calling one of the
functions that compose the async
JavaScript API. When an ident
is
specified with the async
call, the JavaScript code will have the possibility
to emit a signal whose name is ident
when the asynchronous block
completes or simply progresses.
A async
statement blocks until its JavaScript body invokes the
this.notify
method. In other words, an async
statement completes
when its JavaScript body invokes the notify
method.
Example
This example spawns a JavaScript timer that will complete no sooner
than five seconds after being started. When the timeout is reached,
the JavaScript asynchronous computation resumes the reactive machine
and emit the signal O
with the value 5
.
Inside the asynchronous block, the JavaScript this
object is
a descriptor of the asynchronous computation. The machine that
has spawned this async
form is stored in this.machine
.
★ Example exec2.hh.js
import * as hh from "@hop/hiphop";
hiphop module prg(resolve) {
out O;
async (O) {
setTimeout(() => this.notify(5), 100);
}
pragma { resolve(false); }
}
export const mach = new hh.ReactiveMachine(prg, "exec");
mach.outbuf = "";
mach.addEventListener("O", function(evt) {
mach.outbuf += ("O emitted!") + "\n";
});
mach.debug_emitted_func = val => val;
mach.batchPromise = new Promise((res, rej) => mach.init(res));
mach.react();
Kill, suspend, and resume
The optional arguments kill
, suspend
, and resume
are JavaScript
statements that are executed when the HipHop statement state
changes. They give the opportunity to the JavaScript program to
cleanup a computation if the async
block is preempted or
suspended or resumed.
The following example shows a JavaScript setTimeout
that is stopped
when the HipHop async
statement is aborted.
★ Example exec-susp-res.hh.js
import * as hh from "@hop/hiphop";
import { format } from "util";
let glob = 5;
hiphop module prg(resolve) {
in RESS; in S; out O; out OT; in T;
fork {
suspend (S.now) {
async (T) {
mach.outbuf += "Oi.\n";
setTimeout(function(self) {
mach.outbuf += "Oi timeout.\n";
self.notify(glob++, false);
}, 100, this);
} suspend {
mach.outbuf += "suspended.\n";
} resume {
mach.outbuf += "resumed.\n";
}
}
} par {
emit O();
}
await (RESS.now);
emit OT(T.nowval);
pragma { resolve(false); }
}
export const mach = new hh.ReactiveMachine(prg, "exec");
mach.outbuf = "";
mach.debug_emitted_func = emitted => {
mach.outbuf += format(emitted) + "\n";
}
mach.batchPromise = new Promise((res, rej) => mach.init(res));
mach.react();
mach.inputAndReact("S");
mach.inputAndReact("S");
mach.inputAndReact("S");
mach.inputAndReact("S");
mach.react();
mach.react();
mach.inputAndReact("S");
setTimeout(function() {
mach.react();
mach.react();
mach.inputAndReact("RESS");
mach.inputAndReact("S");
mach.react();
}, 100);
Async JavaScript API
JavaScript asynchronous blocks can use several functions to notify the reactive machine that their state have changed.
Inside the body of an async
form, this
is bound to an async
descriptor and the reactive machine executing asynchronous block is
to be found in this.machine
. New reactions can be triggered
from within the async
JavaScript block. Example:
★ Example setinterval.hh.js
import * as hh from "@hop/hiphop";
import { format } from "util";
hiphop module setinterval(resolve) {
inout A, Tick;
fork {
abort count(3, Tick.now) {
async (A) {
this.tmt = setInterval(() => this.react(Tick.signame), 100);
} kill {
clearInterval(this.tmt);
}
}
}
pragma { resolve(false); }
};
export const mach = new hh.ReactiveMachine(setinterval);
mach.outbuf = "";
mach.debug_emitted_func = val => {
mach.outbuf += format(val) + "\n";
}
mach.batchPromise = new Promise((res, rej) => mach.init(res));
mach.react();
This example uses the expression Tick.signame
that is a JavaScript
expression that evaluates to the HipHop internal name of the signal
Tick
.
async.notify(value, [react = true])
This function notifies the reactive machine that the async
form has
completed and it emits the event that was associated with the form.
Notifying the termination of the
async
form with the methodnotify
is not equivalent to triggering a new reaction as only thenotify
method tells HipHop to execute the next statement in sequence.
How and when the machine is notified depends of value
's type. Two
cases are considered:
value
is anything but a JavaScript Promise: the machine is immediately notified and the value of theasync
associate event isvalue
.value
is a Promise: the machine is notified only when the Promise resolves or rejects. The value of the associated event is an object whose propertyresolve
istrue
if the Promise has resolved andfalse
otherwise. The propertyval
is the value with which the Promise has resolved or rejected.
Here is an example of an async
block that uses a JavaScript Promise to
resume the HipHop computation.
★ Example exec3.hh.js
import * as hh from "@hop/hiphop";
import { format } from "util";
hiphop module prg(resolve) {
out O;
async (O) {
this.notify(new Promise(function(resolve, reject) {
setTimeout(() => resolve(5), 100);
}));
}
pragma { resolve(false); }
}
export const mach = new hh.ReactiveMachine(prg, "exec");
mach.addEventListener("O", function(evt) {
mach.outbuf += ("O=" + evt.nowval.val + " emitted!") + "\n";
});
mach.batchPromise = new Promise((res, rej) => mach.init(res));
mach.debug_emitted_func = val => val;
mach.outbuf = "";
mach.react();
The optional argument react
controls whether a reaction should be
automatically triggered with the notification. If the react
is true
,
a reaction to the machine is executed. The following asynchronous block:
async {
this.notify("complete");
}
is equivalent to:
async {
this.notify("complete", false);
this.react();
}
async.react(sigset)
Invokes the react
method with sigset
argument of the machine
running the async
block.