Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 57 additions & 9 deletions src/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand All @@ -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()
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
})

23 changes: 18 additions & 5 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1054,11 +1054,12 @@ export async function runWithDelayedLoading<T = any>(asyncTask: () => Promise<T>
}: IRunWithDelayedLoadingOptions = {}): Promise<T> {
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> | T) => void )| null = null
let resolve: ((d: Awaited<T> | T) => void ) | null = null
let reject: ((e: Error) => void) | null = null

// taskStartTime = Date.now()
Expand All @@ -1069,13 +1070,23 @@ export async function runWithDelayedLoading<T = any>(asyncTask: () => Promise<T>
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
Expand All @@ -1093,7 +1104,8 @@ export async function runWithDelayedLoading<T = any>(asyncTask: () => Promise<T>
}
loading = false
onSettled?.();
resolve!(res)
resolve!(res);
// resSettled = true
} catch (e) {
if (loadingStartTime !== null) {
const elapsedTime = Date.now() - loadingStartTime
Expand All @@ -1103,7 +1115,8 @@ export async function runWithDelayedLoading<T = any>(asyncTask: () => Promise<T>
}
loading = false
onSettled?.();
reject!(e as Error)
reject!(e as Error);
// resSettled = true
}
}

Expand Down