Improved py:all-done event (#1778)

This commit is contained in:
Andrea Giammarchi
2023-10-02 13:38:28 +02:00
committed by GitHub
parent fdc35ce3ed
commit 50f7ab0f34
5 changed files with 62 additions and 65 deletions

View File

@@ -11,7 +11,7 @@
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"polyscript": "^0.4.8",
"polyscript": "^0.4.11",
"type-checked-collections": "^0.1.7"
},
"devDependencies": {
@@ -1782,9 +1782,9 @@
"integrity": "sha512-yyVAOFKTAElc7KdLt2+UKGExNYwYb/Y/WE9i+1ezCQsJE8gbKSjewfpRqK2nQgZ4d4hhAAGgDCOcIZVilqE5UA=="
},
"node_modules/polyscript": {
"version": "0.4.8",
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.4.8.tgz",
"integrity": "sha512-YlgjdMeEnv/i6WOqkh7gc52iSPY1l/psA+egu7z1GNrjwq6udw4WuQPz3rHRbaFhTUdYsVulLd8SBugjbVH6sQ==",
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/polyscript/-/polyscript-0.4.11.tgz",
"integrity": "sha512-wNvCUJp003OR/Q9C0eZJ84MHYeJiMtPTt1pqtsRQ0odRV/M1b3qVQ23oD5DAjq1weXQv1EdfpILwFOpw6VnirA==",
"dependencies": {
"@ungap/structured-clone": "^1.2.0",
"@ungap/with-resolvers": "^0.1.0",

View File

@@ -33,7 +33,7 @@
"dependencies": {
"@ungap/with-resolvers": "^0.1.0",
"basic-devtools": "^0.1.6",
"polyscript": "^0.4.8",
"polyscript": "^0.4.11",
"type-checked-collections": "^0.1.7"
},
"devDependencies": {

View File

@@ -1,62 +1,17 @@
import TYPES from "./types.js";
import hooks from "./hooks.js";
const DONE = "py:all-done";
const {
onAfterRun,
onAfterRunAsync,
codeAfterRunWorker,
codeAfterRunWorkerAsync,
} = hooks;
const waitForIt = [];
const codes = [];
const codeFor = (element) => {
const isAsync = element.hasAttribute("async");
const { promise, resolve } = Promise.withResolvers();
const type = `${DONE}:${waitForIt.push(promise)}`;
// resolve each promise once notified
addEventListener(type, resolve, { once: true });
if (element.hasAttribute("worker")) {
const code = `
from pyscript import window as _w
_w.dispatchEvent(_w.Event.new("${type}"))
`;
if (isAsync) codeAfterRunWorkerAsync.add(code);
else codeAfterRunWorker.add(code);
return code;
for (const [TYPE] of TYPES) {
const selectors = [`script[type="${TYPE}"]`, `${TYPE}-script`];
for (const element of document.querySelectorAll(selectors.join(","))) {
const { promise, resolve } = Promise.withResolvers();
waitForIt.push(promise);
element.addEventListener(`${TYPE}:done`, resolve, { once: true });
}
// dispatch only once the ready element is the same
const code = (_, el) => {
if (el === element) dispatchEvent(new Event(type));
};
if (isAsync) onAfterRunAsync.add(code);
else onAfterRun.add(code);
return code;
};
const selector = [];
for (const [TYPE] of TYPES)
selector.push(`script[type="${TYPE}"]`, `${TYPE}-script`);
// loop over all known scripts and elements
for (const element of document.querySelectorAll(selector.join(",")))
codes.push(codeFor(element));
}
// wait for all the things then cleanup
Promise.all(waitForIt).then(() => {
// cleanup unnecessary hooks
for (const code of codes) {
onAfterRun.delete(code);
onAfterRunAsync.delete(code);
codeAfterRunWorker.delete(code);
codeAfterRunWorkerAsync.delete(code);
}
dispatchEvent(new Event(DONE));
dispatchEvent(new Event("py:all-done"));
});

View File

@@ -90,6 +90,11 @@ const exportedConfig = {};
export { exportedConfig as config, hooks };
for (const [TYPE, interpreter] of TYPES) {
const dispatchDone = (element, isAsync, result) => {
if (isAsync) result.then(() => dispatch(element, TYPE, "done"));
else dispatch(element, TYPE, "done");
};
const { config, plugins, error } = configs.get(TYPE);
// create a unique identifier when/if needed
@@ -211,9 +216,13 @@ for (const [TYPE, interpreter] of TYPES) {
defineProperty(element, "target", { value: show });
// notify before the code runs
dispatch(element, TYPE);
wrap[`run${isAsync ? "Async" : ""}`](
await fetchSource(element, wrap.io, true),
dispatch(element, TYPE, "ready");
dispatchDone(
element,
isAsync,
wrap[`run${isAsync ? "Async" : ""}`](
await fetchSource(element, wrap.io, true),
),
);
} else {
// resolve PyScriptElement to allow connectedCallback
@@ -246,18 +255,21 @@ for (const [TYPE, interpreter] of TYPES) {
async connectedCallback() {
if (!this.executed) {
this.executed = true;
const isAsync = this.hasAttribute("async");
const { io, run, runAsync } = await this._wrap.promise;
const runner = this.hasAttribute("async") ? runAsync : run;
this.srcCode = await fetchSource(
this,
io,
!this.childElementCount,
);
this.replaceChildren();
// notify before the code runs
dispatch(this, TYPE);
runner(this.srcCode);
this.style.display = "block";
dispatch(this, TYPE, "ready");
dispatchDone(
this,
isAsync,
(isAsync ? runAsync : run)(this.srcCode),
);
}
}
}

View File

@@ -0,0 +1,30 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PyScript Next</title>
<script>
addEventListener("py:all-done", ({ type }) => console.log(type));
</script>
<link rel="stylesheet" href="../dist/core.css">
<script type="module" src="../dist/core.js"></script>
</head>
<body>
<script type="py" worker async>
from pyscript import display
display('hello 1')
import js
import time
js.console.log('sleeping...')
time.sleep(2)
js.console.log('...done')
</script>
<p>hello 2</p>
<script type="py" worker async>
from pyscript import display
display('hello 3')
</script>
</body>
</html>