Skip to content

Commit 3e6cbf1

Browse files
authored
fix(useAsyncState): track latest execution to avoid newer results being replaced by outdated ones (#5047)
1 parent 4b3e976 commit 3e6cbf1

File tree

2 files changed

+45
-4
lines changed

2 files changed

+45
-4
lines changed

packages/core/useAsyncState/index.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,4 +119,38 @@ describe('useAsyncState', () => {
119119
expect(state.value).toBe(100)
120120
expect(initialState).toBe(state)
121121
})
122+
123+
it('does not set `state` from an outdated execution', async () => {
124+
const { execute, state } = useAsyncState((returnValue: string, timeout: number) => promiseTimeout(timeout).then(() => returnValue), '')
125+
await Promise.all([
126+
execute(0, 'foo', 100),
127+
execute(0, 'bar', 50),
128+
])
129+
expect(state.value).toBe('bar')
130+
})
131+
132+
it('does not set `isReady` from an outdated execution', async () => {
133+
const { execute, isReady } = useAsyncState(promiseTimeout, shallowRef<void>())
134+
void execute(0, 0)
135+
void execute(0, 100)
136+
await promiseTimeout(50)
137+
expect(isReady.value).toBe(false)
138+
})
139+
140+
it('does not set `isLoading` from an outdated execution', async () => {
141+
const { execute, isLoading } = useAsyncState(promiseTimeout, shallowRef<void>())
142+
void execute(0, 0)
143+
void execute(0, 100)
144+
await promiseTimeout(50)
145+
expect(isLoading.value).toBe(true)
146+
})
147+
148+
it('does not set `error` from an outdated execution', async () => {
149+
const { execute, error } = useAsyncState(promiseTimeout, shallowRef<void>())
150+
await Promise.all([
151+
execute(0, 100, true),
152+
execute(0, 0),
153+
])
154+
expect(error.value).toBeUndefined()
155+
})
122156
})

packages/core/useAsyncState/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ export function useAsyncState<Data, Params extends any[] = any[], Shallow extend
9898
const isLoading = shallowRef(false)
9999
const error = shallowRef<unknown | undefined>(undefined)
100100

101+
let executionsCount = 0
101102
async function execute(delay = 0, ...args: any[]) {
103+
const executionId = (executionsCount += 1)
104+
102105
if (resetOnExecute)
103106
state.value = toValue(initialState)
104107
error.value = undefined
@@ -114,18 +117,22 @@ export function useAsyncState<Data, Params extends any[] = any[], Shallow extend
114117

115118
try {
116119
const data = await _promise
117-
state.value = data
118-
isReady.value = true
120+
if (executionId === executionsCount) {
121+
state.value = data
122+
isReady.value = true
123+
}
119124
onSuccess(data)
120125
}
121126
catch (e) {
122-
error.value = e
127+
if (executionId === executionsCount)
128+
error.value = e
123129
onError(e)
124130
if (throwError)
125131
throw e
126132
}
127133
finally {
128-
isLoading.value = false
134+
if (executionId === executionsCount)
135+
isLoading.value = false
129136
}
130137

131138
return state.value as Data

0 commit comments

Comments
 (0)