Skip to content

Commit

Permalink
continuously fork promises
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Mar 30, 2023
1 parent 1579648 commit 216ef73
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 39 deletions.
139 changes: 124 additions & 15 deletions demo/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
},
"dependencies": {
"@preact/preset-vite": "^2.5.0",
"@urql/preact": "^3.0.3",
"express": "^4.18.2",
"graphql": "^16.6.0",
"preact": "^10.12.1"
"preact": "^10.12.1",
"urql": "latest"
}
}
2 changes: 1 addition & 1 deletion demo/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { h } from 'preact';
import { Suspense, lazy } from 'preact/compat';
import { Client, Provider, cacheExchange, fetchExchange } from '@urql/preact';
import { Client, Provider, cacheExchange, fetchExchange } from 'urql';

const client = new Client({
url: 'https://trygql.formidable.dev/graphql/basic-pokedex',
Expand Down
4 changes: 2 additions & 2 deletions demo/src/Pokemons.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { gql, useQuery } from '@urql/preact';
import { gql, useQuery } from 'urql';
import { h } from 'preact';

const POKEMONS_QUERY = gql`
Expand All @@ -17,7 +17,7 @@ const Counter = () => {
});

const { data, fetching, error } = result;

console.log('hydrated!');
return (
<div>
{fetching && <p>Loading...</p>}
Expand Down
4 changes: 2 additions & 2 deletions demo/src/entry-server.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function render({ res, head }) {
res.socket.on('error', (error) => {
console.error('Fatal', error);
});
const { pipe } = renderToPipeableStream(<App head={head} />, {
const { pipe, abort } = renderToPipeableStream(<App head={head} />, {
onShellReady() {
res.statusCode = 200;
res.setHeader('Content-type', 'text/html');
Expand All @@ -21,5 +21,5 @@ export function render({ res, head }) {

// Abandon and switch to client rendering if enough time passes.
// Try lowering this to see the client recover.
// setTimeout(abort, 10000);
setTimeout(abort, 20000);
}
9 changes: 6 additions & 3 deletions demo/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ function ssrPlugin() {
return next();
}

const { render, abort } = await server.ssrLoadModule(
const { render } = await server.ssrLoadModule(
path.resolve(__dirname, './src/entry-server')
);

setTimeout(abort, 10000);
// setTimeout(abort, 10000);

const indexHtml = await fs.readFile(
path.resolve(__dirname, './index.html'),
Expand All @@ -44,7 +44,10 @@ export default defineConfig({
noExternal: /./
},
build: {
ssrManifest: true
ssrManifest: true,
commonjsOptions: {
transformMixedEsModules: true
}
},
resolve: {
alias: {
Expand Down
34 changes: 20 additions & 14 deletions src/lib/chunked.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,27 @@ export async function renderToChunks(vnode, { context, onWrite, abortSignal }) {
if (len > 0) {
onWrite('<div hidden>');
onWrite(createInitScript(len));
await Promise.all(renderer.suspended.map((s) => s.promise));
// We should keep checking all promises
await forkPromises(renderer);
onWrite('</div>');
}
}

async function forkPromises(renderer) {
if (renderer.suspended.length > 0) {
const suspensions = [...renderer.suspended];
await Promise.all(renderer.suspended.map((s) => s.promise));
renderer.suspended = renderer.suspended.filter(
(s) => !suspensions.includes(s)
);
await forkPromises(renderer);
}
}

/** @type {RendererErrorHandler} */
function handleError(error, vnode, renderChild) {
if (!error || !error.then) return;

console.log('--- IN HANDLER ---');
// walk up to the Suspense boundary
while ((vnode = vnode[PARENT])) {
let component = vnode[COMPONENT];
Expand All @@ -50,15 +61,8 @@ function handleError(error, vnode, renderChild) {

if (!vnode) return;

let root = vnode;
// TODO: we can't unset parent because of this traversal to Suspense
while (root !== null && !root.mask && root[PARENT] !== null) {
root = root[PARENT];
}

const mask = root.mask || (root.mask = [0, 0]);
const id = 'S' + mask[0] + '-' + mask[1]++ + this.suspended.length;

const id = vnode.__v;
const found = this.suspended.find((x) => x.id === id);
const race = new Deferred();

const abortSignal = this.abortSignal;
Expand All @@ -70,9 +74,9 @@ function handleError(error, vnode, renderChild) {

const promise = error.then(
() => {
console.log('resolved');
if (abortSignal && abortSignal.aborted) return;
this.onWrite(createSubtree(id, renderChild(vnode.props.children)));
const child = renderChild(vnode.props.children);
if (child) this.onWrite(createSubtree(id, child));
},
// TODO: Abort and send hydration code snippet to client
// to attempt to recover during hydration
Expand All @@ -87,5 +91,7 @@ function handleError(error, vnode, renderChild) {

const fallback = renderChild(vnode.props.fallback);

return `<!--preact-island:${id}-->${fallback}<!--/preact-island:${id}-->`;
return found
? ''
: `<!--preact-island:${id}-->${fallback}<!--/preact-island:${id}-->`;
}

0 comments on commit 216ef73

Please sign in to comment.