Skip to content

Commit f868f26

Browse files
committed
better compatibility with promiseLike and synchronous functions, catch errors thrown synchronously
see #24
1 parent 4daae4f commit f868f26

File tree

2 files changed

+48
-28
lines changed

2 files changed

+48
-28
lines changed

src/index.ts

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -246,36 +246,32 @@ const useAsyncInternal = <R = UnknownResult, Args extends any[] = UnknownArgs>(
246246
isMounted() && CurrentPromise.is(p);
247247

248248
const executeAsyncOperation = (...args: Args): Promise<R> => {
249-
const promise: MaybePromise<R> = asyncFunction(...args);
249+
// async ensures errors thrown synchronously are caught (ie, bug when formatting api payloads)
250+
// async ensures promise-like and synchronous functions are handled correctly too
251+
// see https://github.com/slorber/react-async-hook/issues/24
252+
const promise: Promise<R> = (async () => asyncFunction(...args))();
250253
setCurrentParams(args);
251-
if (promise instanceof Promise) {
252-
CurrentPromise.set(promise);
253-
AsyncState.setLoading();
254-
promise.then(
255-
result => {
256-
if (shouldHandlePromise(promise)) {
257-
AsyncState.setResult(result);
258-
}
259-
normalizedOptions.onSuccess(result, {
260-
isCurrent: () => CurrentPromise.is(promise),
261-
});
262-
},
263-
error => {
264-
if (shouldHandlePromise(promise)) {
265-
AsyncState.setError(error);
266-
}
267-
normalizedOptions.onError(error, {
268-
isCurrent: () => CurrentPromise.is(promise),
269-
});
254+
CurrentPromise.set(promise);
255+
AsyncState.setLoading();
256+
promise.then(
257+
result => {
258+
if (shouldHandlePromise(promise)) {
259+
AsyncState.setResult(result);
270260
}
271-
);
272-
return promise;
273-
} else {
274-
// We allow passing a non-async function (mostly for useAsyncCallback convenience)
275-
const syncResult: R = promise;
276-
AsyncState.setResult(syncResult);
277-
return Promise.resolve<R>(syncResult);
278-
}
261+
normalizedOptions.onSuccess(result, {
262+
isCurrent: () => CurrentPromise.is(promise),
263+
});
264+
},
265+
error => {
266+
if (shouldHandlePromise(promise)) {
267+
AsyncState.setError(error);
268+
}
269+
normalizedOptions.onError(error, {
270+
isCurrent: () => CurrentPromise.is(promise),
271+
});
272+
}
273+
);
274+
return promise;
279275
};
280276

281277
const getLatestExecuteAsyncOperation = useGetter(executeAsyncOperation);

test/useAsync.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ describe('useAync', () => {
4646
expect(onError).not.toHaveBeenCalled();
4747
});
4848

49+
it('should resolve a successful Jest mocked resolved value', async () => {
50+
const onSuccess = jest.fn();
51+
const onError = jest.fn();
52+
53+
const asyncFunction = jest.fn().mockResolvedValue(fakeResults);
54+
55+
const { result, waitForNextUpdate } = renderHook(() =>
56+
useAsync(asyncFunction, [], {
57+
onSuccess: () => onSuccess(),
58+
onError: () => onError(),
59+
})
60+
);
61+
62+
expect(result.current.loading).toBe(true);
63+
64+
await waitForNextUpdate();
65+
66+
expect(result.current.result).toEqual(fakeResults);
67+
expect(result.current.loading).toBe(false);
68+
expect(result.current.error).toBeUndefined();
69+
expect(onSuccess).toHaveBeenCalled();
70+
expect(onError).not.toHaveBeenCalled();
71+
});
72+
4973
// TODO legacy: should we remove this behavior?
5074
it('should resolve a successful synchronous request', async () => {
5175
const onSuccess = jest.fn();

0 commit comments

Comments
 (0)