31 lines
1.3 KiB
TypeScript
31 lines
1.3 KiB
TypeScript
import { ref } from 'vue'
|
|||
|
|
import { getApiErrorDetails } from '@/shared/api/http'
|
||
|
|
|
||
|
|
// 只等待工作清单同步守卫;版本冲突、权限失败和写请求均不重试。
|
||
|
|
export function useWorkSyncRead() {
|
||
|
|
const syncing = ref(false)
|
||
|
|
let generation = 0
|
||
|
|
async function read<T>(load: () => Promise<T>, signal: AbortSignal): Promise<T> {
|
||
|
|
const current = ++generation
|
||
|
|
syncing.value = false
|
||
|
|
try {
|
||
|
|
for (let attempt = 0; ; attempt++) {
|
||
|
|
signal.throwIfAborted()
|
||
|
|
try { return await load() }
|
||
|
|
catch (cause) {
|
||
|
|
const error = getApiErrorDetails(cause)
|
||
|
|
if (error.code !== 'A0409' || error.message !== '预约批次或运输关联已变化,服务端正在协调完整工作清单' || attempt >= 15) throw cause
|
||
|
|
if (current === generation) syncing.value = true
|
||
|
|
await new Promise<void>((resolve, reject) => {
|
||
|
|
const abort = () => { clearTimeout(timer); reject(signal.reason) }
|
||
|
|
const timer = setTimeout(() => { signal.removeEventListener('abort', abort); resolve() }, 2000)
|
||
|
|
signal.addEventListener('abort', abort, { once: true })
|
||
|
|
if (signal.aborted) { signal.removeEventListener('abort', abort); abort() }
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} finally { if (current === generation) syncing.value = false }
|
||
|
|
}
|
||
|
|
return { syncing, read }
|
||
|
|
}
|