Skip to content

Commit

Permalink
feat: support retry
Browse files Browse the repository at this point in the history
  • Loading branch information
haoziqaq committed Jul 6, 2023
1 parent f4c8c87 commit aae87f0
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 41 deletions.
26 changes: 26 additions & 0 deletions playground/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import {
useApiUpdateUser,
useApiPatchUser,
useApiDownloadFile,
useApiThrowError,
User,
} from './apis'
const id = ref('1')
const deleteId = ref('1')
const [users, apiGetUsers, { loading: isUsersLoading }] = useApiGetUsers<User[]>([], { immediate: true })
const [user, apiGetUser, { loading: isUserLoading, abort }] = useApiGetUser<User>(
{},
{
Expand All @@ -24,6 +26,7 @@ const [user, apiGetUser, { loading: isUserLoading, abort }] = useApiGetUser<User
},
}
)
const [addedUser, apiAddUser] = useApiAddUser<User>(
{},
{
Expand All @@ -37,6 +40,7 @@ const [addedUser, apiAddUser] = useApiAddUser<User>(
},
}
)
const [updatedUser, apiUpdateUser] = useApiUpdateUser<User>(
{},
{
Expand All @@ -50,6 +54,7 @@ const [updatedUser, apiUpdateUser] = useApiUpdateUser<User>(
},
}
)
const [patchedUser] = useApiPatchUser<User>(
{},
{
Expand All @@ -63,6 +68,7 @@ const [patchedUser] = useApiPatchUser<User>(
},
}
)
const [deletedUser, apiDeleteUser] = useApiDeleteUser<User>(
{},
{
Expand All @@ -76,10 +82,21 @@ const [deletedUser, apiDeleteUser] = useApiDeleteUser<User>(
},
}
)
const [file, apiDownloadFile, { downloadProgress }] = useApiDownloadFile<Blob | null>(null, {
onTransform: (response) => response,
})
const [errorUser, apiThrowError, { loading: isThrowErrorLoading }] = useApiThrowError(
{},
{
onBefore(refs) {
refs.data.value = {}
},
retry: 3,
}
)
const userRecord = reactive<User>({
id: '',
name: '',
Expand Down Expand Up @@ -147,4 +164,13 @@ watch(
<h3>File Size: {{ file?.size ?? 0 }}</h3>
<var-button type="primary" @click="() => apiDownloadFile()">Download (PS: Please use slow 3G)</var-button>
</var-space>

<var-divider margin="30px 0" />

<var-space direction="column">
<var-cell>name: throw error</var-cell>
<var-cell>loading: {{ isThrowErrorLoading }}</var-cell>
<var-cell>data: {{ errorUser ?? 'No Data' }}</var-cell>
<var-button type="primary" @click="() => apiThrowError()">Retry</var-button>
</var-space>
</template>
9 changes: 9 additions & 0 deletions playground/src/apis/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,13 @@ export function useApiDownloadFile<D>(data: D, options?: Options<D, Blob>) {
runner: axle.getBlob,
...options,
})
}

export function useApiThrowError<D>(data: D, options?: Options<D, Response<User>>) {
return useAxle({
data,
url: '/user/throw-error',
runner: axle.get,
...options,
})
}
1 change: 1 addition & 0 deletions playground/src/request/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ axle.axios.interceptors.response.use(
return response.data
},
(error) => {
console.log(error)
Snackbar.error(error.message)
return Promise.reject(error)
}
Expand Down
15 changes: 15 additions & 0 deletions playground/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const users = [
},
]

let count = 0

router.get('/user/list-user', async (ctx) => {
await delay(300)
response(ctx, 200, users)
Expand All @@ -54,6 +56,19 @@ router.get('/user/get-user', async (ctx) => {
response(ctx, 200, user)
})

router.get('/user/throw-error', async (ctx) => {
await delay(200)

count++

if (count % 4 === 0) {
response(ctx, 200, users[0])
return
}

ctx.status = 500
})

router.post('/user/add-user', async (ctx) => {
await delay(300)

Expand Down
108 changes: 67 additions & 41 deletions src/use.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,37 @@ export interface RunOptions<P> {
url?: string
params?: P
config?: AxleRequestConfig
_retryCount?: number
}

export type Run<D, P> = (options?: RunOptions<P>) => Promise<D>
export type Run<R, P> = (options?: RunOptions<P>) => Promise<R>

export interface UseAxleRefs<D> {
data: Ref<D>
loading: Ref<boolean>
error: Ref<Error | undefined>
uploadProgress: Ref<number>
downloadProgress: Ref<number>
}

export interface UseAxleOptions<D = any, R = any, P = Record<string, any>> {
url: string
runner: ReturnType<typeof createFetchHelper> | ReturnType<typeof createModifyHelper>
data: D
params?: P
retry?: number
config?: AxleRequestConfig
immediate?: boolean
onBefore?(): void
onAfter?(): void
onTransform?(response: R, prev: D): D
onSuccess?(response: R): void
onError?(error: Error): void
onBefore?(refs: UseAxleRefs<D>): void
onAfter?(refs: UseAxleRefs<D>): void
onTransform?(response: R, refs: UseAxleRefs<D>): D
onSuccess?(response: R, refs: UseAxleRefs<D>): void
onError?(error: Error, refs: UseAxleRefs<D>): void
}

export type UseAxleReturn<D, P> = [
export type UseAxleInstance<D, R, P> = [
data: Ref<D>,
run: Run<D, P>,
run: Run<R, P>,
extra: {
uploadProgress: Ref<number>
downloadProgress: Ref<number>
Expand All @@ -42,14 +52,17 @@ export interface CreateUseAxleOptions {
export function createUseAxle(options: CreateUseAxleOptions = {}) {
const { onTransform: defaultOnTransform } = options

const useAxle = <D = any, R = any, P = Record<string, any>>(options: UseAxleOptions<D, R, P>) => {
const useAxle = <D = any, R = any, P = Record<string, any>>(
options: UseAxleOptions<D, R, P>
): UseAxleInstance<D, R, P> => {
const {
url,
runner,
immediate,
data: initialData,
params: initialParams,
config: initialConfig,
retry = 0,
onBefore = () => {},
onAfter = () => {},
onTransform = (defaultOnTransform as UseAxleOptions<D, R, P>['onTransform']) ??
Expand All @@ -64,14 +77,19 @@ export function createUseAxle(options: CreateUseAxleOptions = {}) {
const error = ref<Error>()
const downloadProgress = ref(0)
const uploadProgress = ref(0)
const shouldRetry = retry > 0

let controller = new AbortController()

const abort = () => {
controller.abort()
const refs: UseAxleRefs<D> = {
data,
loading,
error,
downloadProgress,
uploadProgress
}

const run: Run<D, P> = (options: RunOptions<P> = {}) => {
let controller = new AbortController()

const run: Run<R, P> = async (options: RunOptions<P> = {}) => {
if (controller.signal.aborted) {
controller = new AbortController()
}
Expand All @@ -81,42 +99,50 @@ export function createUseAxle(options: CreateUseAxleOptions = {}) {

const url = options.url ?? initialUrl

onBefore()
onBefore(refs)

loading.value = true

return runner(url, options.params, {
signal: controller.signal,
try {
const response = await runner(url, options.params, {
signal: controller.signal,

onUploadProgress(event) {
uploadProgress.value = event.progress ?? 0
},
onUploadProgress(event) {
uploadProgress.value = event.progress ?? 0
},

onDownloadProgress(event) {
downloadProgress.value = event.progress ?? 0
},
onDownloadProgress(event) {
downloadProgress.value = event.progress ?? 0
},

...options.config,
})
.then((response) => {
data.value = onTransform(response as R, data.value)
error.value = undefined
...options.config,
})

onSuccess(response as R)
data.value = onTransform(response as R, data.value)
error.value = undefined
onSuccess(response as R, refs)
loading.value = false
onAfter(refs)

return data.value
})
.catch((responseError) => {
error.value = responseError
return response as R
} catch (responseError: unknown) {
const currentRetryCount = options._retryCount == null ? 0 : options._retryCount

onError(responseError)
if (shouldRetry && currentRetryCount < retry) {
return run({ ...options, _retryCount: currentRetryCount + 1 })
}

throw responseError
})
.finally(() => {
loading.value = false
onAfter()
})
error.value = responseError as Error
onError(responseError as Error, refs)
loading.value = false
onAfter(refs)

throw responseError
}
}

const abort = () => {
controller.abort()
}

if (immediate) {
Expand All @@ -137,7 +163,7 @@ export function createUseAxle(options: CreateUseAxleOptions = {}) {
downloadProgress,
abort,
},
] as UseAxleReturn<D, P>
]
}

return useAxle
Expand Down

0 comments on commit aae87f0

Please sign in to comment.