Skip to content

Commit 96a5e18

Browse files
Merge pull request #54 from pyscript/notify-ready-done
Better support for ready/done events
2 parents 3f7dad4 + c91b062 commit 96a5e18

File tree

12 files changed

+67
-12
lines changed

12 files changed

+67
-12
lines changed

docs/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* [XWorker](#xworker) - how `XWorker` class and its `xworker` reference work
1212
* [Custom Scripts](#custom-scripts) - how *custom types* can be defined and used to enrich any core feature
1313
* [Ready Event](#ready-event) - how to listen to the `type:ready` event
14+
* [Done Event](#done-event) - how to listen to the `type:done` event
1415
* [Examples](#examples) - some *polyscript* based live example
1516
* [Interpreter Features](#interpreter-features) - current state of supported interpreters
1617

@@ -500,6 +501,10 @@ The [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)
500501

501502
The `worker` detail is essential to know if an `xworker` property is attached so that it's also easy to pollute its `sync` proxy utility.
502503

504+
## Done Event
505+
506+
Whenever a *non-custom* script is going to run some code, or whenever *any worker* is going to run its own code, a `type:done` event is dispatched through the element *after* its code has fully executed.
507+
503508
### Custom Types on Main
504509

505510
The reason this event is not automatically dispatched on custom type elements or scripts is that these will have their own `onInterpreterReady` hook to eventually do more before desiring, or needing, to notify the "*readiness*" of such custom element and, in case of wanting the event to happen, this is the tiny boilerplate needed to simulate otherwise non-custom type events:

docs/core.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/core.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

esm/script-handler.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ const execute = async (script, source, XWorker, isAsync) => {
6161
get: () => script,
6262
});
6363
module.registerJSModule(interpreter, 'polyscript', { XWorker });
64-
dispatch(script, type);
65-
return module[isAsync ? 'runAsync' : 'run'](interpreter, content);
64+
dispatch(script, type, 'ready');
65+
const result = module[isAsync ? 'runAsync' : 'run'](interpreter, content);
66+
const done = dispatch.bind(null, script, type, 'done');
67+
if (isAsync) result.then(done);
68+
else done();
69+
return result;
6670
} finally {
6771
delete document.currentScript;
6872
}

esm/utils.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ const nodeInfo = (node, type) => ({
2323
* Notify the main thread about element "readiness".
2424
* @param {HTMLScriptElement | HTMLElement} target the script or custom-type element
2525
* @param {string} type the custom/type as event prefix
26+
* @param {string} what the kind of event to dispatch, i.e. `ready` or `done`
2627
* @param {boolean} [worker = false] `true` if dispatched form a worker, `false` by default if in main
2728
* @param {globalThis.CustomEvent} [CustomEvent = globalThis.CustomEvent] the `CustomEvent` to use
2829
*/
29-
const dispatch = (target, type, worker = false, CE = CustomEvent) => {
30+
const dispatch = (target, type, what, worker = false, CE = CustomEvent) => {
3031
target.dispatchEvent(
31-
new CE(`${type}:ready`, {
32+
new CE(`${type}:${what}`, {
3233
bubbles: true,
3334
detail: { worker },
3435
})

esm/worker/_template.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ add('message', ({ data: { options, config: baseURL, code, hooks } }) => {
100100

101101
const { CustomEvent, document } = window;
102102
const element = id && document.getElementById(id) || null;
103+
const notify = kind => dispatch(element, custom || type, kind, true, CustomEvent);
103104

104105
let target = '';
105106

@@ -130,10 +131,14 @@ add('message', ({ data: { options, config: baseURL, code, hooks } }) => {
130131
// allows transforming arguments with sync
131132
transform = details.transform.bind(details, interpreter);
132133

133-
if (element) dispatch(element, custom || type, true, CustomEvent);
134+
// notify worker ready to execute
135+
if (element) notify('ready');
134136

135137
// run either sync or async code in the worker
136138
await details[name](interpreter, code);
139+
140+
// notify worker done executing
141+
if (element) notify('done');
137142
return interpreter;
138143
} catch (error) {
139144
postMessage(error);

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "polyscript",
3-
"version": "0.4.10",
3+
"version": "0.4.11",
44
"description": "PyScript single core to rule them all",
55
"main": "./cjs/index.js",
66
"types": "./types/polyscript/esm/index.d.ts",
@@ -71,6 +71,6 @@
7171
"html-escaper": "^3.0.3"
7272
},
7373
"worker": {
74-
"blob": "sha256-xGArLSJr40wTgAn6eZ7sGIKJMD8yVtiKjA/5Dr6ZbBc="
74+
"blob": "sha256-ixJNXrBnwM18zoc4l44JmnNzgD+eoNpGaOcZz3dXP94="
7575
}
7676
}

test/integration.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>polyscript integration tests</title>
77
</head>
8-
<body><ul><li><strong>micropython</strong><ul><li><a href="/test/integration/interpreter/micropython/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/micropython/config-object.html">config-object</a></li><li><a href="/test/integration/interpreter/micropython/custom-hooks.html">custom-hooks</a></li><li><a href="/test/integration/interpreter/micropython/fetch.html">fetch</a></li><li><a href="/test/integration/interpreter/micropython/no-type.html">no-type</a></li><li><a href="/test/integration/interpreter/micropython/worker-attribute.html">worker-attribute</a></li><li><a href="/test/integration/interpreter/micropython/worker-bad.html">worker-bad</a></li><li><a href="/test/integration/interpreter/micropython/worker-empty-attribute.html">worker-empty-attribute</a></li><li><a href="/test/integration/interpreter/micropython/worker-error.html">worker-error</a></li><li><a href="/test/integration/interpreter/micropython/worker-lua.html">worker-lua</a></li><li><a href="/test/integration/interpreter/micropython/worker-tag.html">worker-tag</a></li><li><a href="/test/integration/interpreter/micropython/worker-window.html">worker-window</a></li><li><a href="/test/integration/interpreter/micropython/worker.html">worker</a></li></ul><li><strong>pyodide</strong><ul><li><a href="/test/integration/interpreter/pyodide/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/pyodide/button.html">button</a></li><li><a href="/test/integration/interpreter/pyodide/fetch.html">fetch</a></li><li><a href="/test/integration/interpreter/pyodide/sync.html">sync</a></li><li><a href="/test/integration/interpreter/pyodide/worker-error.html">worker-error</a></li><li><a href="/test/integration/interpreter/pyodide/worker-transform.html">worker-transform</a></li><li><a href="/test/integration/interpreter/pyodide/worker.html">worker</a></li></ul><li><strong>ruby-wasm-wasi</strong><ul><li><a href="/test/integration/interpreter/ruby-wasm-wasi/bootstrap.html">bootstrap</a></li></ul><li><strong>wasmoon</strong><ul><li><a href="/test/integration/interpreter/wasmoon/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/wasmoon/worker.html">worker</a></li></ul></ul></body>
8+
<body><ul><li><strong>micropython</strong><ul><li><a href="/test/integration/interpreter/micropython/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/micropython/config-object.html">config-object</a></li><li><a href="/test/integration/interpreter/micropython/custom-hooks.html">custom-hooks</a></li><li><a href="/test/integration/interpreter/micropython/fetch.html">fetch</a></li><li><a href="/test/integration/interpreter/micropython/no-type.html">no-type</a></li><li><a href="/test/integration/interpreter/micropython/ready-done.html">ready-done</a></li><li><a href="/test/integration/interpreter/micropython/worker-attribute.html">worker-attribute</a></li><li><a href="/test/integration/interpreter/micropython/worker-bad.html">worker-bad</a></li><li><a href="/test/integration/interpreter/micropython/worker-empty-attribute.html">worker-empty-attribute</a></li><li><a href="/test/integration/interpreter/micropython/worker-error.html">worker-error</a></li><li><a href="/test/integration/interpreter/micropython/worker-lua.html">worker-lua</a></li><li><a href="/test/integration/interpreter/micropython/worker-tag.html">worker-tag</a></li><li><a href="/test/integration/interpreter/micropython/worker-window.html">worker-window</a></li><li><a href="/test/integration/interpreter/micropython/worker.html">worker</a></li></ul><li><strong>pyodide</strong><ul><li><a href="/test/integration/interpreter/pyodide/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/pyodide/button.html">button</a></li><li><a href="/test/integration/interpreter/pyodide/fetch.html">fetch</a></li><li><a href="/test/integration/interpreter/pyodide/sync.html">sync</a></li><li><a href="/test/integration/interpreter/pyodide/worker-error.html">worker-error</a></li><li><a href="/test/integration/interpreter/pyodide/worker-transform.html">worker-transform</a></li><li><a href="/test/integration/interpreter/pyodide/worker.html">worker</a></li></ul><li><strong>ruby-wasm-wasi</strong><ul><li><a href="/test/integration/interpreter/ruby-wasm-wasi/bootstrap.html">bootstrap</a></li></ul><li><strong>wasmoon</strong><ul><li><a href="/test/integration/interpreter/wasmoon/bootstrap.html">bootstrap</a></li><li><a href="/test/integration/interpreter/wasmoon/worker.html">worker</a></li></ul></ul></body>
99
</html>

test/integration/_shared.js

+10
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ exports.python = {
102102
await expect(result).toContain('clicked');
103103
},
104104

105+
waitForReadyDone: ({ expect }, url) => async ({ page }) => {
106+
await page.goto(url);
107+
await page.waitForSelector('html.done');
108+
const [ready, done] = await page.evaluate(() => [ready, done]);
109+
await expect(ready.length).toBe(4);
110+
await expect(done.length).toBe(4);
111+
await expect(ready.join(',')).toBe('micropython:ready,micropython:ready,micropython:ready,micropython:ready');
112+
await expect(done.join(',')).toBe('micropython:done,micropython:done,micropython:done,micropython:done');
113+
},
114+
105115
error: ({ expect }, baseURL) => async ({ page }) => {
106116
// Test that when the worker throws an error, the page does not crash and the
107117
// error is reported to the console.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<script type="module">
7+
import { init } from '../utils.js';
8+
init('micropython');
9+
10+
globalThis.ready = [];
11+
addEventListener('micropython:ready', ({ type }) => {
12+
ready.push(type);
13+
});
14+
15+
globalThis.done = [];
16+
addEventListener('micropython:done', ({ type }) => {
17+
if (done.push(type) === document.querySelectorAll('script[type="micropython"]').length)
18+
document.documentElement.classList.add('done');
19+
});
20+
</script>
21+
</head>
22+
<body>
23+
<script type="micropython">print(1)</script>
24+
<script type="micropython" async>print(2)</script>
25+
<script type="micropython" worker>print(3)</script>
26+
<script type="micropython" worker async>print(4)</script>
27+
</body>
28+
</html>

test/integration/micropython.js

+2
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ module.exports = (playwright, baseURL) => {
3535
const result = await page.evaluate(() => document.body.innerText);
3636
await expect(result.trim()).toBe('OK');
3737
});
38+
39+
test('MicroPython ready-done events', python.waitForReadyDone(playwright, `${baseURL}/ready-done.html`));
3840
};

0 commit comments

Comments
 (0)