From 2d21a36e3a8e2b68a57be15f9da468d57bd14459 Mon Sep 17 00:00:00 2001 From: Jay Meistrich Date: Fri, 20 Sep 2024 20:43:17 +0200 Subject: [PATCH] fix: error not being set if get failed without retry --- src/sync/retry.ts | 80 ++++++++++++++++++++------------------ src/sync/syncObservable.ts | 6 ++- tests/crud.test.ts | 34 ++++++++++++++++ 3 files changed, 80 insertions(+), 40 deletions(-) diff --git a/src/sync/retry.ts b/src/sync/retry.ts index 5a38a653..0c85c026 100644 --- a/src/sync/retry.ts +++ b/src/sync/retry.ts @@ -28,46 +28,50 @@ export function runWithRetry( fn: (params: OnErrorRetryParams) => T | Promise, onError: (error: Error, retryParams: OnErrorRetryParams) => void, ): T | Promise { - let value = fn(state); + try { + let value = fn(state); - if (isPromise(value) && retryOptions) { - let timeoutRetry: number; - if (mapRetryTimeouts.has(state.node)) { - clearTimeout(mapRetryTimeouts.get(state.node)); - } - return new Promise((resolve, reject) => { - const run = () => { - (value as Promise) - .then((val: any) => { - resolve(val); - }) - .catch((error: Error) => { - state.retryNum++; - if (timeoutRetry) { - clearTimeout(timeoutRetry); - } - if (onError) { - onError(error, state); - } - if (!state.cancelRetry) { - const timeout = createRetryTimeout(retryOptions, state.retryNum, () => { - value = fn(state); - run(); - }); + if (isPromise(value) && retryOptions) { + let timeoutRetry: number; + if (mapRetryTimeouts.has(state.node)) { + clearTimeout(mapRetryTimeouts.get(state.node)); + } + return new Promise((resolve, reject) => { + const run = () => { + (value as Promise) + .then((val: any) => { + resolve(val); + }) + .catch((error: Error) => { + state.retryNum++; + if (timeoutRetry) { + clearTimeout(timeoutRetry); + } + if (onError) { + onError(error, state); + } + if (!state.cancelRetry) { + const timeout = createRetryTimeout(retryOptions, state.retryNum, () => { + value = fn(state); + run(); + }); - if (timeout === false) { - state.cancelRetry = true; - reject(error); - } else { - mapRetryTimeouts.set(state.node, timeout); - timeoutRetry = timeout; + if (timeout === false) { + state.cancelRetry = true; + reject(error); + } else { + mapRetryTimeouts.set(state.node, timeout); + timeoutRetry = timeout; + } } - } - }); - }; - run(); - }); - } + }); + }; + run(); + }); + } - return value; + return value; + } catch (error) { + return Promise.reject(error); + } } diff --git a/src/sync/syncObservable.ts b/src/sync/syncObservable.ts index 890fc843..4a04a142 100644 --- a/src/sync/syncObservable.ts +++ b/src/sync/syncObservable.ts @@ -1126,7 +1126,9 @@ export function syncObservable( } const existingValue = getNodeValue(node); - const onError = (error: Error) => onGetError(error, { getParams, source: 'get' }); + const onError = (error: Error) => { + onGetError(error, { getParams, source: 'get' }); + }; const getParams: SyncedGetParams = { node, @@ -1211,7 +1213,7 @@ export function syncObservable( }); }; if (isPromise(got)) { - got.then(handle); + got.then(handle).catch(onError); } else { handle(got); } diff --git a/tests/crud.test.ts b/tests/crud.test.ts index c8044d23..b9cea0b8 100644 --- a/tests/crud.test.ts +++ b/tests/crud.test.ts @@ -2540,3 +2540,37 @@ describe('waitForSet', () => { expect(typeAtWaitForSet).toEqual('create'); }); }); +describe('Error is set', () => { + test('error is set if get fails', async () => { + const obs$ = observable( + syncedCrud({ + get: () => { + // await promiseTimeout(1); + throw new Error('test'); + }, + }), + ); + + obs$.get(); + + await promiseTimeout(1); + + expect(syncState(obs$).error.get()).toEqual(new Error('test')); + }); + test('error is set if get fails async', async () => { + const obs$ = observable( + syncedCrud({ + get: async () => { + await promiseTimeout(1); + throw new Error('test'); + }, + }), + ); + + obs$.get(); + + await promiseTimeout(2); + + expect(syncState(obs$).error.get()).toEqual(new Error('test')); + }); +});