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
asyncform with the methodnotifyis not equivalent to triggering a new reaction as only thenotifymethod 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:
valueis anything but a JavaScript Promise: the machine is immediately notified and the value of theasyncassociate event isvalue.valueis a Promise: the machine is notified only when the Promise resolves or rejects. The value of the associated event is an object whose propertyresolveistrueif the Promise has resolved andfalseotherwise. The propertyvalis 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.