-
Notifications
You must be signed in to change notification settings - Fork 12
Illustrative Examples
This page presents several examples that showcase Stopify's features.
The browser does not have a blocking sleep
function. However, we can use
window.setTimeout
and Stopify to simulate a blocking sleep
operation:
function sleep(duration) {
asyncRun.pauseImmediate(() => {
window.setTimeout(() => asyncRun.continueImmediate({ type: 'normal', value: undefined }), duration);
});
}
In the code above, asyncRun
is an instance of
AsyncRun.
Note that this function should be stopified itself and needs to be declared as
an external. A complete example of a page that uses sleep
is shown below.
<html>
<body>
<script src="https://github.com/plasma-umass/Stopify/releases/download/0.7.2/stopify-full.bundle.js"></script>
<script>
function sleep(duration) {
asyncRun.pauseImmediate(() => {
window.setTimeout(() => asyncRun.continueImmediate({ type: 'normal', value: undefined }), duration);
});
}
const program = `
while(true) {
sleep(1000);
document.body.appendChild(document.createTextNode("."));
}
`;
const asyncRun = stopify.stopifyLocally(program);
asyncRun.g = { sleep, document, window, asyncRun };
asyncRun.run(() => { });
</script>
</body>
</html>
This program runs forever and prints a period each second.
The prompt
and alert
functions that are built-in to browsers are not the
ideal way to receive input from the user. First, modal dialog boxes are
unattractive; second, a user can dismiss them; and finally, if a page displays
too many modal dialog boxes, the browser can give the user to suppress all of
them.
<html>
<body>
<div id="webConsole"></div>
<script src="https://github.com/plasma-umass/Stopify/releases/download/0.7.2/stopify-full.bundle.js"></script>
<script>
const webConsole = document.getElementById('webConsole');
function alert(message) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(message));
webConsole.appendChild(div);
}
function prompt() {
return runner.pauseImmediate(() => {
const div = document.createElement('div');
div.appendChild(document.createTextNode('> '));
const input = document.createElement('input');
div.appendChild(input);
// When ENTER pressed, replace the <input> with plain text
input.addEventListener('keypress', (event) => {
if (event.keyCode === 13) {
const value = input.value;
div.appendChild(document.createTextNode(value));
div.removeChild(input);
runner.continueImmediate({ type: 'normal', value: value });
}
});
webConsole.appendChild(div);
input.focus();
});
}
const program = `
alert("Enter the first number");
var x = Number(prompt());
alert("Enter the second number");
var y = Number(prompt());
alert("Result is " + (x + y));
`;
const runner = stopify.stopifyLocally(program);
runner.g = { Number, prompt, alert, runner, document, webConsole };
runner.run(() => console.log('program complete'));
</script>
</body>
</html>
This program prompts the user for two inputs without modal dialog boxes.
<html>
<body>
<button id="pauseResume">Pause / Resume</button>
<div id="webConsole"></div>
<script src="https://github.com/plasma-umass/Stopify/releases/download/0.7.2/stopify-full.bundle.js"></script>
<script>
const webConsole = document.getElementById('webConsole');
var paused = false;
document.getElementById('pauseResume').addEventListener('click', () => {
if (paused) {
paused = false;
runner.resume();
}
else {
runner.pause(() => {
paused = true;
});
}
});
function alert(message) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(message));
webConsole.appendChild(div);
}
function onClick(callback) {
window.addEventListener('click', evt => {
runner.processEvent(
() => callback(evt.clientX, evt.clientY),
() => { /* result from callback does not matter */ });
});
}
const program = `
onClick(function(x, y) {
alert('You clicked at (' + x + ', ' + y + ')');
});
`;
const runner = stopify.stopifyLocally(program);
runner.g = { onClick, alert, window, runner };
runner.run(() => console.log('program complete'));
</script>
</body>
</html>
The onClick
function in the code below is an example of an event handler
that receives a stopified callback named callback
. However, onClick
cannot apply callback
directly. To support suspending execution,
callback
must execute in the context of Stopify's runtime system.
Instead, onClick
invokes Stopify's processEvent
method, passing it a
thunk that calls callback
. If the program is paused, processEvent
queues
the event until the program is resumed. Therefore, if the user pauses the
program, clicks several times, and then resumes, several clicks will clicks
will occur immediately after resumption. It is straightforward to discard
clicks that occur while paused by testing the paused
variable before
invoking processEvent
.