Skip to content

Commit b07df68

Browse files
committed
[Live] Fixing edge case parallel rendering bug
1 parent f63cb59 commit b07df68

File tree

3 files changed

+63
-2
lines changed

3 files changed

+63
-2
lines changed

src/LiveComponent/assets/dist/live_controller.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1946,7 +1946,6 @@ class Component {
19461946
this.valueStore.flushDirtyPropsToPending();
19471947
this.isRequestPending = false;
19481948
this.backendRequest.promise.then(async (response) => {
1949-
this.backendRequest = null;
19501949
const backendResponse = new BackendResponse(response);
19511950
const html = await backendResponse.getBody();
19521951
for (const input of Object.values(this.pendingFiles)) {
@@ -1960,10 +1959,12 @@ class Component {
19601959
if (controls.displayError) {
19611960
this.renderError(html);
19621961
}
1962+
this.backendRequest = null;
19631963
thisPromiseResolve(backendResponse);
19641964
return response;
19651965
}
19661966
this.processRerender(html, backendResponse);
1967+
this.backendRequest = null;
19671968
thisPromiseResolve(backendResponse);
19681969
if (this.isRequestPending) {
19691970
this.isRequestPending = false;

src/LiveComponent/assets/src/Component/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,6 @@ export default class Component {
390390
this.isRequestPending = false;
391391

392392
this.backendRequest.promise.then(async (response) => {
393-
this.backendRequest = null;
394393
const backendResponse = new BackendResponse(response);
395394
const html = await backendResponse.getBody();
396395

@@ -410,6 +409,7 @@ export default class Component {
410409
this.renderError(html);
411410
}
412411

412+
this.backendRequest = null;
413413
thisPromiseResolve(backendResponse);
414414

415415
return response;
@@ -418,6 +418,7 @@ export default class Component {
418418
this.processRerender(html, backendResponse);
419419

420420
// finally resolve this promise
421+
this.backendRequest = null;
421422
thisPromiseResolve(backendResponse);
422423

423424
// do we already have another request pending?

src/LiveComponent/assets/test/controller/render.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,65 @@ describe('LiveController rendering Tests', () => {
329329
await waitFor(() => expect(test.element).toHaveTextContent('Title: "greetings to you"'));
330330
});
331331

332+
it('waits for the rendering process of previous request to finish before starting a new one', async () => {
333+
const test = await createTest({
334+
title: 'greetings',
335+
contents: '',
336+
}, (data: any) => `
337+
<div ${initComponent(data)}>
338+
<input data-model='title' value='${data.title}'>
339+
340+
Title: "${data.title}"
341+
342+
<button data-action='live#$render'>Reload</button>
343+
</div>
344+
`);
345+
346+
let didSecondRenderStart = false;
347+
let secondRenderStartedAt = 0;
348+
test.component.on('render:started', () => {
349+
if (didSecondRenderStart) {
350+
return;
351+
}
352+
didSecondRenderStart = true;
353+
354+
test.component.on('loading.state:started', () => {
355+
secondRenderStartedAt = Date.now();
356+
});
357+
358+
test.expectsAjaxCall();
359+
test.component.render();
360+
});
361+
362+
let firstRenderFinishedAt = 0;
363+
test.component.on('render:finished', () => {
364+
// set the finish time for the first render only
365+
if (firstRenderFinishedAt === 0) {
366+
firstRenderFinishedAt = Date.now();
367+
}
368+
369+
// the sleep guarantees that if the 2nd request was correctly
370+
// delayed, its start time will be at least 10ms after the first
371+
// render finished. Without this, even if the 2nd request is
372+
// correctly delayed, the "first render finish" and "second render
373+
// start" times could be the same, because no time has passed.
374+
const sleep = (milliseconds: number) => {
375+
const startTime = new Date().getTime();
376+
while (new Date().getTime() < startTime + milliseconds);
377+
}
378+
sleep(10);
379+
});
380+
381+
test.expectsAjaxCall();
382+
383+
await test.component.render();
384+
385+
await waitFor(() => expect(didSecondRenderStart).toBe(true));
386+
await waitFor(() => expect(firstRenderFinishedAt).not.toBe(0));
387+
await waitFor(() => expect(secondRenderStartedAt).not.toBe(0));
388+
expect(secondRenderStartedAt).toBeGreaterThan(firstRenderFinishedAt);
389+
});
390+
332391
it('can update svg', async () => {
333392
const test = await createTest({ text: 'SVG' }, (data: any) => `
334393
<div ${initComponent(data)}>

0 commit comments

Comments
 (0)