diff --git a/packages/framework/global/src/env/index.ts b/packages/framework/global/src/env/index.ts index db647e11c494..a7f43b437149 100644 --- a/packages/framework/global/src/env/index.ts +++ b/packages/framework/global/src/env/index.ts @@ -26,5 +26,4 @@ export const IS_IPAD = export const IS_WINDOWS = /Win/.test(platform); -export const REQUEST_IDLE_CALLBACK_ENABLED = - 'requestIdleCallback' in globalThis; +export const RIC_ENABLED = 'requestIdleCallback' in globalThis; diff --git a/packages/framework/global/src/utils/function.ts b/packages/framework/global/src/utils/function.ts index 449782788af4..a8f0088cd960 100644 --- a/packages/framework/global/src/utils/function.ts +++ b/packages/framework/global/src/utils/function.ts @@ -1,3 +1,5 @@ +import { IS_WEB, RIC_ENABLED } from '../env/index.js'; + export async function sleep(ms: number, signal?: AbortSignal): Promise { return new Promise(resolve => { if (signal?.aborted) { @@ -112,3 +114,37 @@ export const debounce = void>( timer = setTimeout(setTimer, limit); } as T; }; + +interface IdleDeadline { + timeRemaining: () => number; + didTimeout: boolean; +} + +type IdleRequestCallback = (deadline: IdleDeadline) => void; + +interface IdleRequestOptions { + timeout?: number; +} + +function requestIdleCallbackPolyfill( + callback: IdleRequestCallback, + options?: IdleRequestOptions +): number { + const start = Date.now(); + return setTimeout(() => { + callback({ + didTimeout: false, + timeRemaining: () => Math.max(0, 50 - (Date.now() - start)), + }); + }, options?.timeout ?? 1) as unknown as number; +} + +export const requestIdleCallback = + IS_WEB && RIC_ENABLED + ? window.requestIdleCallback + : requestIdleCallbackPolyfill; + +export const cancelIdleCallback = + IS_WEB && RIC_ENABLED + ? window.cancelIdleCallback + : (id: number) => clearTimeout(id); diff --git a/packages/framework/store/src/__tests__/collection.unit.spec.ts b/packages/framework/store/src/__tests__/collection.unit.spec.ts index 0edb232a5fa9..57d22ebbf20a 100644 --- a/packages/framework/store/src/__tests__/collection.unit.spec.ts +++ b/packages/framework/store/src/__tests__/collection.unit.spec.ts @@ -2,7 +2,7 @@ import type { Slot } from '@blocksuite/global/utils'; -import { assert, beforeEach, describe, expect, it, vi } from 'vitest'; +import { assert, describe, expect, it, vi } from 'vitest'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import type { BlockModel, BlockSchemaType, Doc } from '../index.js'; @@ -70,28 +70,6 @@ function createTestDoc(docId = defaultDocId) { return doc; } -function requestIdleCallbackPolyfill( - callback: IdleRequestCallback, - options?: IdleRequestOptions -) { - const timeout = options?.timeout ?? 1000; - const start = Date.now(); - return setTimeout(function () { - callback({ - didTimeout: false, - timeRemaining: function () { - return Math.max(0, timeout - (Date.now() - start)); - }, - }); - }, timeout) as unknown as number; -} - -beforeEach(() => { - if (globalThis.requestIdleCallback === undefined) { - globalThis.requestIdleCallback = requestIdleCallbackPolyfill; - } -}); - describe('basic', () => { it('can init collection', () => { const options = createTestOptions(); diff --git a/packages/framework/store/src/transformer/job.ts b/packages/framework/store/src/transformer/job.ts index ba429c0d088f..fd9b25c83bb2 100644 --- a/packages/framework/store/src/transformer/job.ts +++ b/packages/framework/store/src/transformer/job.ts @@ -1,5 +1,6 @@ +import { IS_WEB } from '@blocksuite/global/env'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; -import { Slot } from '@blocksuite/global/utils'; +import { requestIdleCallback, Slot } from '@blocksuite/global/utils'; import type { BlockModel, BlockSchemaType } from '../schema/index.js'; import type { Doc, DocCollection, DocMeta } from '../store/index.js'; @@ -486,10 +487,7 @@ export class Job { children, }); - const nextTick = - typeof window !== 'undefined' - ? window.requestAnimationFrame - : setImmediate; + const nextTick = IS_WEB ? requestIdleCallback : setImmediate; await new Promise(resolve => nextTick(() => resolve(undefined))); doc.addBlock( modelData.flavour as BlockSuite.Flavour,