fix(问题修复): 修复页面刷新与缩略图竞态
build.yaml / build (push) Successful in 45s

This commit is contained in:
xiaoqidun committed 2026-10-26 13:45:47 +08:00
1 parent 01e29618f4
commit 1061b07942
2 files changed
+24 -29

No files matched your search

+1 -1
View File
@@ -155,6 +155,7 @@ function createDocumentState() {
selectedPages: new Set(), selectedPages: new Set(),
documentSelection: null, documentSelection: null,
pageInFlight: new Map(), pageInFlight: new Map(),
pageLoadSeq: 0,
pageRenderQueue: [], pageRenderQueue: [],
pagePrefetchIndex: null, pagePrefetchIndex: null,
pagePrefetchDirection: 1, pagePrefetchDirection: 1,
@@ -170,7 +171,6 @@ function createDocumentState() {
thumbnailFrame: 0, thumbnailFrame: 0,
pageDragIndex: null, pageDragIndex: null,
pageListCurrent: null, pageListCurrent: null,
thumbnailInFlight: new Set(),
thumbnailObserver: null, thumbnailObserver: null,
visibleThumbnails: new Set(), visibleThumbnails: new Set(),
exportFormats: [], exportFormats: [],
+23 -28
View File
@@ -49,7 +49,7 @@ export function createPageView(context) {
} }
pageView.prunePageRenderQueue(); pageView.prunePageRenderQueue();
const page = await pageView.renderFlowPage(index, { throwError: true, openSeq, priority: 0 }); const page = await pageView.renderFlowPage(index, { throwError: true, openSeq, priority: 0 });
if (openSeq !== state.openSeq || index !== state.pageIndex) { if (!page || openSeq !== state.openSeq || index !== state.pageIndex) {
return null; return null;
} }
if (options.scroll !== false) { if (options.scroll !== false) {
@@ -222,6 +222,7 @@ export function createPageView(context) {
}, },
resetPageLoading() { resetPageLoading() {
context.state.pageLoadSeq++;
context.releaseMediaPlayers(); context.releaseMediaPlayers();
if (context.state.pagePresentFrame) cancelAnimationFrame(context.state.pagePresentFrame); if (context.state.pagePresentFrame) cancelAnimationFrame(context.state.pagePresentFrame);
context.state.pagePresentFrame = 0; context.state.pagePresentFrame = 0;
@@ -306,26 +307,28 @@ export function createPageView(context) {
async renderFlowPage(index, options = {}) { async renderFlowPage(index, options = {}) {
const openSeq = options.openSeq || context.state.openSeq; const openSeq = options.openSeq || context.state.openSeq;
if (openSeq !== context.state.openSeq) { const loadSeq = context.state.pageLoadSeq;
const current = () => openSeq === context.state.openSeq && loadSeq === context.state.pageLoadSeq;
if (!current()) {
return null; return null;
} }
try { try {
const page = await pageView.loadPageData(index, { openSeq, priority: options.priority ?? 2, view: true, const page = await pageView.loadPageData(index, { openSeq, priority: options.priority ?? 2, view: true,
refresh: context.state.editing && context.state.pageCache.get(index)?.previewOnly }); refresh: context.state.editing && context.state.pageCache.get(index)?.previewOnly });
if (openSeq !== context.state.openSeq) { if (!current()) {
return null; return null;
} }
if (page) { if (page) {
await pageView.queuePagePresentation(index, page, openSeq, true); await pageView.queuePagePresentation(index, page, openSeq, true);
} }
if (openSeq !== context.state.openSeq) return null; if (!current()) return null;
pageView.trimPageCache(); pageView.trimPageCache();
return page; return page;
} catch (err) { } catch (err) {
if (openSeq === context.state.openSeq) { if (current()) {
pageView.markFlowPageError(index, err); pageView.markFlowPageError(index, err);
} }
if (options.throwError && openSeq === context.state.openSeq) { if (options.throwError && current()) {
throw err; throw err;
} }
return null; return null;
@@ -334,6 +337,7 @@ export function createPageView(context) {
loadPageData(index, options = {}) { loadPageData(index, options = {}) {
const openSeq = options.openSeq || context.state.openSeq; const openSeq = options.openSeq || context.state.openSeq;
const loadSeq = context.state.pageLoadSeq;
if (openSeq !== context.state.openSeq) { if (openSeq !== context.state.openSeq) {
return Promise.resolve(null); return Promise.resolve(null);
} }
@@ -343,7 +347,7 @@ export function createPageView(context) {
if (current) { if (current) {
current.priority = Math.min(current.priority, priority); current.priority = Math.min(current.priority, priority);
current.view &&= !!options.view; current.view &&= !!options.view;
if (current.preview && !options.preview) return current.promise.then(page => page && openSeq === context.state.openSeq ? pageView.loadPageData(index, options) : null); if (current.preview && !options.preview) return current.promise.then(page => page && openSeq === context.state.openSeq && loadSeq === context.state.pageLoadSeq ? pageView.loadPageData(index, options) : null);
return current.promise; return current.promise;
} }
if (!options.refresh && context.state.pageCache.has(index) && (options.preview || !context.state.pageCache.get(index).previewOnly)) { if (!options.refresh && context.state.pageCache.has(index) && (options.preview || !context.state.pageCache.get(index).previewOnly)) {
@@ -373,16 +377,16 @@ export function createPageView(context) {
return; return;
} }
context.state.pageRenderRunning = true; context.state.pageRenderRunning = true;
const tab = context.documentTab(), openSeq = tab.state.openSeq; const tab = context.documentTab(), openSeq = tab.state.openSeq, loadSeq = tab.state.pageLoadSeq;
requestAnimationFrame(() => { requestAnimationFrame(() => {
if (context.activeTab === tab && openSeq === tab.state.openSeq) pageView.processPageRenderQueue(); if (context.activeTab === tab && openSeq === tab.state.openSeq && loadSeq === tab.state.pageLoadSeq) pageView.processPageRenderQueue();
}); });
}, },
async processPageRenderQueue() { async processPageRenderQueue() {
const tab = context.documentTab(), openSeq = tab.state.openSeq; const tab = context.documentTab(), openSeq = tab.state.openSeq, loadSeq = tab.state.pageLoadSeq;
await context.nextFrame(); await context.nextFrame();
if (context.activeTab !== tab || openSeq !== tab.state.openSeq) return; if (context.activeTab !== tab || openSeq !== tab.state.openSeq || loadSeq !== tab.state.pageLoadSeq) return;
const available = (nativeHost?.parallelism || context.wasmWorker?.parallelism || 1) - tab.state.pageRenderWorkers; const available = (nativeHost?.parallelism || context.wasmWorker?.parallelism || 1) - tab.state.pageRenderWorkers;
await Promise.all(Array.from({ length: Math.max(0, available) }, () => pageView.processPageRenderTasks())); await Promise.all(Array.from({ length: Math.max(0, available) }, () => pageView.processPageRenderTasks()));
}, },
@@ -598,10 +602,10 @@ export function createPageView(context) {
}, },
schedulePagePresentation() { schedulePagePresentation() {
const tab = context.documentTab(), state = tab.state, openSeq = state.openSeq; const tab = context.documentTab(), state = tab.state, openSeq = state.openSeq, loadSeq = state.pageLoadSeq;
if (state.pagePresentFrame || !state.pagePresentQueue.size) return; if (state.pagePresentFrame || !state.pagePresentQueue.size) return;
state.pagePresentFrame = requestAnimationFrame(() => { state.pagePresentFrame = requestAnimationFrame(() => {
if (context.activeTab !== tab || openSeq !== state.openSeq) return; if (context.activeTab !== tab || openSeq !== state.openSeq || loadSeq !== state.pageLoadSeq) return;
state.pagePresentFrame = 0; state.pagePresentFrame = 0;
const started = performance.now(); const started = performance.now();
do { do {
@@ -1120,7 +1124,6 @@ export function createPageView(context) {
resetThumbnails() { resetThumbnails() {
context.state.visibleThumbnails.clear(); context.state.visibleThumbnails.clear();
context.state.thumbnailInFlight.clear();
if (context.state.thumbnailObserver) { if (context.state.thumbnailObserver) {
context.state.thumbnailObserver.disconnect(); context.state.thumbnailObserver.disconnect();
context.state.thumbnailObserver = null; context.state.thumbnailObserver = null;
@@ -1220,31 +1223,23 @@ export function createPageView(context) {
}, },
async renderThumbnail(index, openSeq = context.state.openSeq) { async renderThumbnail(index, openSeq = context.state.openSeq) {
if (openSeq !== context.state.openSeq || !context.state.doc) { const loadSeq = context.state.pageLoadSeq;
const current = () => openSeq === context.state.openSeq && loadSeq === context.state.pageLoadSeq;
if (!current() || !context.state.doc) {
return; return;
} }
const key = `${openSeq}:${index}`;
if (context.state.thumbnailInFlight.has(key)) {
return;
}
context.state.thumbnailInFlight.add(key);
try { try {
const page = await pageView.loadPageData(index, { openSeq, priority: 5, preview: true, view: true }); const page = await pageView.loadPageData(index, { openSeq, priority: 5, preview: true, view: true });
if (openSeq !== context.state.openSeq) { if (!current() || !page) {
return;
}
if (!page) {
return; return;
} }
await pageView.queuePagePresentation(index, page, openSeq); await pageView.queuePagePresentation(index, page, openSeq);
if (openSeq !== context.state.openSeq) return; if (!current()) return;
pageView.trimPageCache(); pageView.trimPageCache();
} catch { } catch {
if (openSeq === context.state.openSeq) { if (current()) {
pageView.markThumbnailError(index); pageView.markThumbnailError(index);
} }
} finally {
context.state.thumbnailInFlight.delete(key);
} }
}, },