From 7e381e830a99933ab1b235c8ce65a6e4e4330258 Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Fri, 3 Nov 2023 17:42:29 +0800 Subject: [PATCH] perf(core): load all pages after 10s (#4834) --- packages/common/infra/package.json | 2 +- .../favorite/favorite-list.tsx | 4 +- .../core/src/layouts/workspace-layout.tsx | 27 +++++++++ packages/frontend/hooks/package.json | 2 + .../src/use-block-suite-workspace-page.ts | 56 ++++++++++++++++++- packages/frontend/hooks/tsconfig.json | 3 +- yarn.lock | 23 ++++---- 7 files changed, 102 insertions(+), 15 deletions(-) diff --git a/packages/common/infra/package.json b/packages/common/infra/package.json index a954aaeb6b..8a538cc60b 100644 --- a/packages/common/infra/package.json +++ b/packages/common/infra/package.json @@ -59,7 +59,7 @@ "@blocksuite/global": "0.0.0-20231101080734-aa27dc89-nightly", "@blocksuite/store": "0.0.0-20231101080734-aa27dc89-nightly", "jotai": "^2.4.3", - "jotai-effect": "^0.1.0", + "jotai-effect": "^0.2.2", "tinykeys": "^2.1.0", "zod": "^3.22.4" }, diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx index 4b7482c8bb..b4603695b5 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favorite-list.tsx @@ -6,6 +6,8 @@ import { ReferencePage } from '../components/reference-page'; import type { FavoriteListProps } from '../index'; import EmptyItem from './empty-item'; +const emptyPageIdSet = new Set(); + export const FavoriteList = ({ workspace }: FavoriteListProps) => { const metas = useBlockSuitePageMeta(workspace); @@ -35,7 +37,7 @@ export const FavoriteList = ({ workspace }: FavoriteListProps) => { metaMapping={metaMapping} pageId={pageMeta.id} // memo? - parentIds={new Set()} + parentIds={emptyPageIdSet} workspace={workspace} /> ); diff --git a/packages/frontend/core/src/layouts/workspace-layout.tsx b/packages/frontend/core/src/layouts/workspace-layout.tsx index 58c4f5a15d..2e87d161ac 100644 --- a/packages/frontend/core/src/layouts/workspace-layout.tsx +++ b/packages/frontend/core/src/layouts/workspace-layout.tsx @@ -18,6 +18,7 @@ import { rootWorkspacesMetadataAtom, } from '@affine/workspace/atom'; import { assertExists } from '@blocksuite/global/utils'; +import type { Page } from '@blocksuite/store'; import type { DragEndEvent } from '@dnd-kit/core'; import { DndContext, @@ -29,6 +30,7 @@ import { useSensors, } from '@dnd-kit/core'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; +import { loadPage } from '@toeverything/hooks/use-block-suite-workspace-page'; import { currentWorkspaceIdAtom } from '@toeverything/infra/atom'; import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { nanoid } from 'nanoid'; @@ -119,6 +121,29 @@ type WorkspaceLayoutProps = { incompatible?: boolean; }; +// fix https://github.com/toeverything/AFFiNE/issues/4825 +function useLoadWorkspacePages() { + const [currentWorkspace] = useCurrentWorkspace(); + const pageMetas = useBlockSuitePageMeta(currentWorkspace.blockSuiteWorkspace); + useEffect(() => { + if (currentWorkspace) { + const timer = setTimeout(() => { + const pageIds = pageMetas.map(meta => meta.id); + const pages = pageIds + .map(id => currentWorkspace.blockSuiteWorkspace.getPage(id)) + .filter((p): p is Page => !!p); + pages.forEach(page => { + loadPage(page, -10); + }); + }, 10 * 1000); // load pages after 10s + return () => { + clearTimeout(timer); + }; + } + return; + }, [currentWorkspace, pageMetas]); +} + export const WorkspaceLayout = function WorkspacesSuspense({ children, incompatible = false, @@ -237,6 +262,8 @@ export const WorkspaceLayoutInner = ({ const inTrashPage = pageMeta?.trash ?? false; const setMainContainer = useSetAtom(mainContainerAtom); + useLoadWorkspacePages(); + return ( <> {/* This DndContext is used for drag page from all-pages list into a folder in sidebar */} diff --git a/packages/frontend/hooks/package.json b/packages/frontend/hooks/package.json index f4f1d3cf1a..53e94cec1c 100644 --- a/packages/frontend/hooks/package.json +++ b/packages/frontend/hooks/package.json @@ -9,11 +9,13 @@ "foxact": "^0.2.20", "jotai": "^2.4.3", "lodash.debounce": "^4.0.8", + "p-queue": "^7.4.1", "react": "18.2.0", "swr": "2.2.4", "uuid": "^9.0.1" }, "devDependencies": { + "@affine/debug": "workspace:*", "@affine/env": "workspace:*", "@blocksuite/block-std": "0.0.0-20231101080734-aa27dc89-nightly", "@blocksuite/blocks": "0.0.0-20231101080734-aa27dc89-nightly", diff --git a/packages/frontend/hooks/src/use-block-suite-workspace-page.ts b/packages/frontend/hooks/src/use-block-suite-workspace-page.ts index 7ac68fb261..0b614c7c10 100644 --- a/packages/frontend/hooks/src/use-block-suite-workspace-page.ts +++ b/packages/frontend/hooks/src/use-block-suite-workspace-page.ts @@ -1,9 +1,13 @@ +import { DebugLogger } from '@affine/debug'; import { assertExists, DisposableGroup } from '@blocksuite/global/utils'; import type { Page, Workspace } from '@blocksuite/store'; import type { Atom } from 'jotai'; import { atom, useAtomValue } from 'jotai'; +import PQueue from 'p-queue'; import { useEffect } from 'react'; +const logger = new DebugLogger('use-block-suite-workspace-page'); + const weakMap = new WeakMap>>(); const emptyAtom = atom(null); @@ -45,6 +49,52 @@ function getAtom(w: Workspace, pageId: string | null): Atom { return map.get(pageId) as Atom; } } +// concurrently load 3 pages at most +const CONCURRENT_JOBS = 3; + +const loadPageQueue = new PQueue({ + concurrency: CONCURRENT_JOBS, +}); + +const loadedPages = new WeakSet(); + +const awaitForIdle = () => + new Promise(resolve => + requestIdleCallback(resolve, { + timeout: 1000, // do not wait for too long + }) + ); + +const awaitForTimeout = (timeout: number) => + new Promise(resolve => setTimeout(resolve, timeout)); + +/** + * Load a page and wait for it to be loaded + * This page will be loaded in a queue so that it will not jam the network and browser CPU + */ +export function loadPage(page: Page, priority = 0) { + if (loadedPages.has(page)) { + return Promise.resolve(); + } + loadedPages.add(page); + return loadPageQueue.add( + async () => { + if (!page.loaded) { + await awaitForIdle(); + await page.waitForLoaded(); + // we do not know how long it takes to load a page here + // so that we just use 300ms timeout as the default page processing time + await awaitForTimeout(300); + logger.debug('page loaded', page.id); + } else { + // do nothing if it is already loaded + } + }, + { + priority, + } + ); +} export function useBlockSuiteWorkspacePage( blockSuiteWorkspace: Workspace, @@ -55,8 +105,10 @@ export function useBlockSuiteWorkspacePage( const page = useAtomValue(pageAtom); useEffect(() => { - if (!page?.loaded) { - page?.waitForLoaded().catch(console.error); + if (page && !page.loaded) { + loadPage(page).catch(err => { + logger.error('Failed to load page', err); + }); } }, [page]); diff --git a/packages/frontend/hooks/tsconfig.json b/packages/frontend/hooks/tsconfig.json index c25d14e5a5..f0040e1558 100644 --- a/packages/frontend/hooks/tsconfig.json +++ b/packages/frontend/hooks/tsconfig.json @@ -8,6 +8,7 @@ }, "references": [ { "path": "../../common/env" }, - { "path": "../../common/y-indexeddb" } + { "path": "../../common/y-indexeddb" }, + { "path": "../../common/debug" } ] } diff --git a/yarn.lock b/yarn.lock index 5ec48cfd38..6b2ca80911 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12509,6 +12509,7 @@ __metadata: version: 0.0.0-use.local resolution: "@toeverything/hooks@workspace:packages/frontend/hooks" dependencies: + "@affine/debug": "workspace:*" "@affine/env": "workspace:*" "@blocksuite/block-std": "npm:0.0.0-20231101080734-aa27dc89-nightly" "@blocksuite/blocks": "npm:0.0.0-20231101080734-aa27dc89-nightly" @@ -12522,6 +12523,7 @@ __metadata: foxact: "npm:^0.2.20" jotai: "npm:^2.4.3" lodash.debounce: "npm:^4.0.8" + p-queue: "npm:^7.4.1" react: "npm:18.2.0" swr: "npm:2.2.4" uuid: "npm:^9.0.1" @@ -12571,7 +12573,7 @@ __metadata: async-call-rpc: "npm:^6.3.1" electron: "link:../../frontend/electron/node_modules/electron" jotai: "npm:^2.4.3" - jotai-effect: "npm:^0.1.0" + jotai-effect: "npm:^0.2.2" nanoid: "npm:^5.0.1" react: "npm:^18.2.0" rxjs: "npm:^7.8.1" @@ -24423,15 +24425,6 @@ __metadata: languageName: node linkType: hard -"jotai-effect@npm:^0.1.0": - version: 0.1.0 - resolution: "jotai-effect@npm:0.1.0" - peerDependencies: - jotai: ">=2.4.3" - checksum: 38d87a3c64dfd9e1b9467b9b589947f03fdd29134f950bb11591cd335a670ce6cd13261e7e7bc4c0c9852ce0f663be215aa469c43f974ab1ef8823828e81db3d - languageName: node - linkType: hard - "jotai-effect@npm:^0.2.2": version: 0.2.2 resolution: "jotai-effect@npm:0.2.2" @@ -28490,6 +28483,16 @@ __metadata: languageName: node linkType: hard +"p-queue@npm:^7.4.1": + version: 7.4.1 + resolution: "p-queue@npm:7.4.1" + dependencies: + eventemitter3: "npm:^5.0.1" + p-timeout: "npm:^5.0.2" + checksum: 82934551f20a38cc19b31cda7200f2db93ca99b8c642d3ac861d12a7a9160eb32235738a8cd53f1a7ea0c7b52d6c0bb27644b6461e9a51e6a59f1e8d65904b78 + languageName: node + linkType: hard + "p-retry@npm:4, p-retry@npm:^4.5.0": version: 4.6.2 resolution: "p-retry@npm:4.6.2"