Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(useAxios): awaitable execute method #1723

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/contributors.json
Expand Up @@ -166,6 +166,7 @@
"phaust",
"marktnoonan",
"dm4t2",
"melishev",
"mauriciabad",
"mxmvshnvsk",
"AldeonMoriak",
Expand Down Expand Up @@ -223,10 +224,12 @@
"monkeywithacupcake",
"katsuyaU",
"koheing",
"kongmoumou",
"laozei6401",
"leovoon",
"likeswinds",
"lxhyl",
"lxnelyclxud",
"lzdFeiFei",
"meteorlxy",
"odex21",
Expand Down
8 changes: 8 additions & 0 deletions packages/integrations/useAxios/index.md
Expand Up @@ -62,6 +62,14 @@ const { execute } = useAxios(url1, {}, { immediate: false })
execute(url2)
```

The `execute` function resolves with a result of network request.
```ts
import { useAxios } from '@vueuse/integrations/useAxios'

const { execute } = useAxios()
const result = await execute(url)
```

use an instance of axios with `immediate` options

```ts
Expand Down
101 changes: 50 additions & 51 deletions packages/integrations/useAxios/index.test.ts
Expand Up @@ -12,178 +12,177 @@ describe('useAxios', () => {
})
const options = { immediate: false }
const path = '/todos/1'
test('params: url', (done) => {
test('params: url', async () => {
const { isFinished, data, then } = useAxios(url)
expect(isFinished.value).toBeFalsy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(data.value.id).toBe(1)
expect(result.data).toBe(data)
expect(isFinished.value).toBeTruthy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url config', (done) => {
test('params: url config', async () => {
const { isFinished, then } = useAxios(url, config)
expect(isFinished.value).toBeFalsy()
const onRejected = vitest.fn()

then()
await then()
.then((result) => {
expect(result.data.value.id).toBe(1)
expect(isFinished.value).toBeTruthy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url config options', (done) => {
test('params: url config options', async () => {
const { isLoading, execute, then } = useAxios(url, config, options)
expect(isLoading.value).toBeFalsy()
execute('https://jsonplaceholder.typicode.com/todos/2')
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
expect(result.data.value.id).toBe(2)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
const result = await then(undefined, onRejected)
expect(result.data.value.id).toBe(2)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
})

test('params: url instance', (done) => {
test('params: url instance', async () => {
const { isFinished, then } = useAxios(path, instance)
expect(isFinished.value).toBeFalsy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isFinished.value).toBeTruthy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url instance options', (done) => {
test('params: url instance options', async () => {
const { isLoading, execute, then } = useAxios(path, instance, options)
expect(isLoading.value).toBeFalsy()
execute()
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url config instance', (done) => {
test('params: url config instance', async () => {
const { isFinished, then } = useAxios(path, config, instance)
expect(isFinished.value).toBeFalsy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isFinished.value).toBeTruthy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url config instance options', (done) => {
test('params: url config instance options', async () => {
const { isLoading, then, execute } = useAxios(path, config, instance, options)
expect(isLoading.value).toBeFalsy()
execute()
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params: url config instance options, execute: config', (done) => {
test('params: url config instance options, execute: config', async () => {
const { isLoading, then, execute } = useAxios(path, config, instance, options)
expect(isLoading.value).toBeFalsy()
execute(undefined, config)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params no url: nil', (done) => {
const { isLoading, then, execute } = useAxios()
test('params no url: nil', async () => {
const { isLoading, execute } = useAxios()
expect(isLoading.value).toBeFalsy()
execute(url)
const { then } = execute(url)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params no url: config', (done) => {
const { isLoading, then, execute } = useAxios(config)
test('params no url: config', async () => {
const { isLoading, execute } = useAxios(config)
expect(isLoading.value).toBeFalsy()
execute(url)
const { then } = execute(url)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then()
.then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
const result = await then(undefined, onRejected)
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
})

test('params no url: instance', (done) => {
const { isLoading, then, execute } = useAxios(instance)
test('params no url: instance', async () => {
const { isLoading, execute } = useAxios(instance)
expect(isLoading.value).toBeFalsy()
execute(url)
const { then } = execute(path)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})

test('params no url: config instance', (done) => {
test('params no url: config instance', async () => {
const { isLoading, execute } = useAxios(config, instance)
expect(isLoading.value).toBeFalsy()
const res = execute(path)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

const result = await res.then(undefined, onRejected)
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
expect(isLoading.value).toBeFalsy()
})

test('execute is awaitable', async () => {
const { isLoading, then, execute } = useAxios(config, instance)
expect(isLoading.value).toBeFalsy()
execute(path)
expect(isLoading.value).toBeTruthy()
const onRejected = vitest.fn()

then((result) => {
await then((result) => {
expect(result.data.value.id).toBe(1)
expect(isLoading.value).toBeFalsy()
expect(onRejected).toBeCalledTimes(0)
done()
}, onRejected)
})
})
44 changes: 18 additions & 26 deletions packages/integrations/useAxios/index.ts
Expand Up @@ -79,13 +79,13 @@ export interface StrictUseAxiosReturn<T> extends UseAxiosReturn<T> {
/**
* Manually call the axios request
*/
execute: (url?: string, config?: AxiosRequestConfig) => void
execute: (url?: string, config?: AxiosRequestConfig) => PromiseLike<StrictUseAxiosReturn<T>>
}
export interface EasyUseAxiosReturn<T> extends UseAxiosReturn<T> {
/**
* Manually call the axios request
*/
execute: (url: string, config?: AxiosRequestConfig) => void
execute: (url: string, config?: AxiosRequestConfig) => PromiseLike<EasyUseAxiosReturn<T>>
}
export interface UseAxiosOptions {
/**
Expand Down Expand Up @@ -160,28 +160,30 @@ export function useAxios<T = any>(...args: any[]): OverallUseAxiosReturn<T> & Pr
isLoading.value = loading
isFinished.value = !loading
}
const waitUntilFinished = () =>
new Promise<OverallUseAxiosReturn<T>>((resolve, reject) => {
until(isFinished).toBe(true)
// eslint-disable-next-line @typescript-eslint/no-use-before-define
.then(() => resolve(result))
.catch(reject)
})
const then: PromiseLike<OverallUseAxiosReturn<T>>['then'] = (onFulfilled, onRejected) =>
waitUntilFinished().then(onFulfilled, onRejected)
const execute: OverallUseAxiosReturn<T>['execute'] = (executeUrl: string | AxiosRequestConfig | undefined = url, config: AxiosRequestConfig = {}) => {
let _url = url ?? ''
let _config
if (typeof executeUrl === 'string') {
_url = executeUrl
_config = config
}
else {
_config = config
}
const _url = typeof executeUrl === 'string'
? executeUrl
: url ?? ''
loading(true)
instance(_url, { ...defaultConfig, ..._config, cancelToken: cancelToken.token })
instance(_url, { ...defaultConfig, ...config, cancelToken: cancelToken.token })
.then((r: any) => {
response.value = r
data.value = r.data
})
.catch((e: any) => {
error.value = e
})
.finally(() => {
loading(false)
})
.finally(() => loading(false))
return { then }
}
if (options.immediate && url)
(execute as StrictUseAxiosReturn<T>['execute'])()
Expand All @@ -202,19 +204,9 @@ export function useAxios<T = any>(...args: any[]): OverallUseAxiosReturn<T> & Pr
abort,
execute,
} as OverallUseAxiosReturn<T>
function waitUntilFinished() {
return new Promise<OverallUseAxiosReturn<T>>((resolve, reject) => {
until(isFinished).toBe(true)
.then(() => resolve(result))
.catch(error => reject(error))
})
}

return {
...result,
then(onFulfilled, onRejected) {
return waitUntilFinished()
.then(onFulfilled, onRejected)
},
then,
}
}