diff --git a/src/common.test.ts b/src/common.test.ts index 5812b73..6a05a65 100644 --- a/src/common.test.ts +++ b/src/common.test.ts @@ -736,6 +736,9 @@ describe('runWithTimeout', () => { }) describe("runWithDelayedLoading", () => { + afterEach(() => { + vi.useRealTimers() + }) it("should resolve immediately after delayed if task finishes before loadingDelay", async () => { vi.useFakeTimers() @@ -750,14 +753,9 @@ describe("runWithDelayedLoading", () => { }) await vi.advanceTimersByTimeAsync(50) // Task finishes before loadingDelay - // mockTask.mock.results[0].value.then(() => {}) // Resolve the task - - // const result = await promise - // expect(result).toBe("result") expect(onLoading).not.toHaveBeenCalled() expect(onSettled).not.toHaveBeenCalled() - await vi.advanceTimersByTimeAsync(50) // Task finishes immediately after loadingDelay const result = await promise expect(result).toBe("result") expect(onLoading).not.toHaveBeenCalled() @@ -805,14 +803,27 @@ describe("runWithDelayedLoading", () => { await vi.advanceTimersByTimeAsync(100) // Reach loadingDelay expect(onLoading).toBeCalledTimes(1) + expect(onSettled).not.toHaveBeenCalled() await vi.advanceTimersByTimeAsync(200) // Task finishes (total 300ms) - const result = await promise - expect(result).toBe("result") - // Should still wait additional 400ms to meet minLoadingDuration (500ms total) - await vi.advanceTimersByTimeAsync(400) + const isSettled1 = await Promise.race([ + promise.then(() => true, () => true), + Promise.resolve(false).then(r => r), + ]) + expect(isSettled1).toBe(false) // promise should not settle + expect(onSettled).not.toHaveBeenCalled() + + // Should still wait additional 300ms to meet minLoadingDuration (500ms total) + await vi.advanceTimersByTimeAsync(300) expect(onSettled).toBeCalledTimes(1) + const isSettled2 = await Promise.race([ + promise.then(() => true, () => true), + Promise.resolve(false).then(r => r), + ]) + const result = await promise + expect(result).toBe("result") + expect(isSettled2).toBe(true) // promise should be settled }) // this will cause vitest throw error skip temporally @@ -847,5 +858,42 @@ describe("runWithDelayedLoading", () => { await promise // No assertions needed, just verifying it doesn't throw }) + + it("should ensure returned promise not resolve before minLoadingDuration if async task finished before minLoadingDuration ", async () => { + vi.useFakeTimers() + + const mockTask = vi.fn(() => new Promise(resolve => setTimeout(() => resolve("result"), 300))) + const onLoading = vi.fn() + const onSettled = vi.fn() + + const promise = runWithDelayedLoading(mockTask, { + loadingDelay: 100, + onLoading, + onSettled, + minLoadingDuration: 500 + }) + + await vi.advanceTimersByTimeAsync(100) // Reach loadingDelay + expect(onLoading).toBeCalledTimes(1) + expect(onSettled).not.toHaveBeenCalled() + + await vi.advanceTimersByTimeAsync(200) // Task finishes (total 300ms) + const isSettled1 = await Promise.race([ + promise.then(() => true, () => true), + Promise.resolve(false).then(r => r) + ]) + expect(isSettled1).toBe(false) // promise should not settle + expect(onSettled).not.toHaveBeenCalled() + + await vi.advanceTimersByTimeAsync(300) // minLoadingDuration reached (500ms total) + const isSettled2 = await Promise.race([ + promise.then(() => true, () => true), + Promise.resolve(false).then(r => r) + ]) + const result = await promise + expect(result).toBe("result") + expect(isSettled2).toBe(true) // promise should be settled + expect(onSettled).toBeCalledTimes(1) + }) }) diff --git a/src/common.ts b/src/common.ts index 2171c9a..5fe565c 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1054,11 +1054,12 @@ export async function runWithDelayedLoading(asyncTask: () => Promise }: IRunWithDelayedLoadingOptions = {}): Promise { let loading = false // 是否展示loading中 let settled = false // 异步任务是否完成 + // let resSettled = false // 返回的Promise是否已Settled // let taskStartTime: number | null = null // 异步任务开始时间 let loadingStartTime: number | null = null // 展示loading的开始时间 // let loadingDelayTimeout = false // 是否超时时间 - let resolve: ((d: Awaited | T) => void )| null = null + let resolve: ((d: Awaited | T) => void ) | null = null let reject: ((e: Error) => void) | null = null // taskStartTime = Date.now() @@ -1069,13 +1070,23 @@ export async function runWithDelayedLoading(asyncTask: () => Promise loadingStartTime = Date.now() onLoading?.() await runLoading() + } else { + // 忽略,返回的Promise已被处理 } }) const task = asyncTask().then((data) => { - resolve!(data) + if (!loading) { + resolve!(data) + // resSettled = true + } else { + } return data }, (e) => { - reject!(e) + if (!loading) { + reject!(e) + // resSettled = true + } else { + } return Promise.reject(e) }).finally(() => { settled = true @@ -1093,7 +1104,8 @@ export async function runWithDelayedLoading(asyncTask: () => Promise } loading = false onSettled?.(); - resolve!(res) + resolve!(res); + // resSettled = true } catch (e) { if (loadingStartTime !== null) { const elapsedTime = Date.now() - loadingStartTime @@ -1103,7 +1115,8 @@ export async function runWithDelayedLoading(asyncTask: () => Promise } loading = false onSettled?.(); - reject!(e as Error) + reject!(e as Error); + // resSettled = true } }