Skip to content

Commit 8ee7677

Browse files
authored
feat(useFetch): allow useFetch to be awaited (#1056)
1 parent dcc80bb commit 8ee7677

File tree

2 files changed

+51
-13
lines changed

2 files changed

+51
-13
lines changed

packages/core/useFetch/index.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,20 @@ describe('useFetch', () => {
324324

325325
expect(data.value).toStrictEqual(JSON.stringify({ message: 'Hello World' }))
326326
})
327+
328+
test('should await request', async() => {
329+
fetchMock.mockResponse(JSON.stringify({ message: 'Hello World' }), { status: 200 })
330+
const { data } = await useFetch('https://example.com')
331+
332+
expect(data.value).toStrictEqual(JSON.stringify({ message: 'Hello World' }))
333+
expect(fetchMock).toBeCalledTimes(1)
334+
})
335+
336+
test('should await json response', async() => {
337+
fetchMock.mockResponse(JSON.stringify({ message: 'Hello World' }), { status: 200 })
338+
const { data } = await useFetch('https://example.com').json()
339+
340+
expect(data.value).toStrictEqual({ message: 'Hello World' })
341+
expect(fetchMock).toBeCalledTimes(1)
342+
})
327343
})

packages/core/useFetch/index.ts

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ComputedRef, Ref } from 'vue-demi'
22
import type { EventHookOn, Fn, MaybeRef, Stoppable } from '@vueuse/shared'
3-
import { containsProp, createEventHook, useTimeoutFn } from '@vueuse/shared'
3+
import { containsProp, createEventHook, useTimeoutFn, until } from '@vueuse/shared'
44
import { computed, isRef, ref, shallowRef, unref, watch } from 'vue-demi'
55
import { defaultWindow } from '../_configurable'
66

@@ -251,11 +251,11 @@ export function createFetch(config: CreateFetchOptions = {}) {
251251
return useFactoryFetch as typeof useFetch
252252
}
253253

254-
export function useFetch<T>(url: MaybeRef<string>): UseFetchReturn<T>
255-
export function useFetch<T>(url: MaybeRef<string>, useFetchOptions: UseFetchOptions): UseFetchReturn<T>
256-
export function useFetch<T>(url: MaybeRef<string>, options: RequestInit, useFetchOptions?: UseFetchOptions): UseFetchReturn<T>
254+
export function useFetch<T>(url: MaybeRef<string>): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
255+
export function useFetch<T>(url: MaybeRef<string>, useFetchOptions: UseFetchOptions): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
256+
export function useFetch<T>(url: MaybeRef<string>, options: RequestInit, useFetchOptions?: UseFetchOptions): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>>
257257

258-
export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchReturn<T> {
258+
export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>> {
259259
const supportsAbort = typeof AbortController === 'function'
260260

261261
let fetchOptions: RequestInit = {}
@@ -353,15 +353,15 @@ export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchRetu
353353

354354
if (isCanceled || !fetch) {
355355
loading(false)
356-
return Promise.resolve()
356+
return Promise.resolve(null)
357357
}
358358

359359
let responseData: any = null
360360

361361
if (timer)
362362
timer.start()
363363

364-
return new Promise((resolve, reject) => {
364+
return new Promise<Response | null>((resolve, reject) => {
365365
fetch(
366366
context.url,
367367
{
@@ -381,13 +381,15 @@ export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchRetu
381381

382382
if (options.afterFetch)
383383
({ data: responseData } = await options.afterFetch({ data: responseData, response: fetchResponse }))
384+
384385
data.value = responseData
386+
385387
// see: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
386388
if (!fetchResponse.ok)
387389
throw new Error(fetchResponse.statusText)
388390

389391
responseEvent.trigger(fetchResponse)
390-
resolve(fetchResponse)
392+
return resolve(fetchResponse)
391393
})
392394
.catch(async(fetchError) => {
393395
let errorData = fetchError.message || fetchError.name
@@ -399,9 +401,9 @@ export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchRetu
399401

400402
errorEvent.trigger(fetchError)
401403
if (throwOnFailed)
402-
reject(fetchError)
403-
else
404-
resolve(undefined)
404+
return reject(fetchError)
405+
406+
return resolve(null)
405407
})
406408
.finally(() => {
407409
loading(false)
@@ -479,11 +481,25 @@ export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchRetu
479481
}
480482
}
481483

484+
function waitUntilFinished() {
485+
return new Promise<UseFetchReturn<T>>((resolve, reject) => {
486+
until(isFinished).toBe(true)
487+
.then(() => resolve(shell))
488+
.catch(error => reject(error))
489+
})
490+
}
491+
482492
function setType(type: DataType) {
483493
return () => {
484494
if (!isFetching.value) {
485495
config.type = type
486-
return shell as any
496+
return {
497+
...shell,
498+
then(onFulfilled: any, onRejected: any) {
499+
return waitUntilFinished()
500+
.then(onFulfilled, onRejected)
501+
},
502+
} as any
487503
}
488504
return undefined
489505
}
@@ -492,7 +508,13 @@ export function useFetch<T>(url: MaybeRef<string>, ...args: any[]): UseFetchRetu
492508
if (options.immediate)
493509
setTimeout(execute, 0)
494510

495-
return shell
511+
return {
512+
...shell,
513+
then(onFulfilled, onRejected) {
514+
return waitUntilFinished()
515+
.then(onFulfilled, onRejected)
516+
},
517+
}
496518
}
497519

498520
function joinPaths(start: string, end: string): string {

0 commit comments

Comments
 (0)