@tanstack/query-core
Version:
The framework agnostic core that powers TanStack Query
135 lines • 3.65 kB
JavaScript
// src/retryer.ts
import { focusManager } from "./focusManager.js";
import { onlineManager } from "./onlineManager.js";
import { pendingThenable } from "./thenable.js";
import { isServer, sleep } from "./utils.js";
function defaultRetryDelay(failureCount) {
return Math.min(1e3 * 2 ** failureCount, 3e4);
}
function canFetch(networkMode) {
return (networkMode ?? "online") === "online" ? onlineManager.isOnline() : true;
}
var CancelledError = class extends Error {
constructor(options) {
super("CancelledError");
this.revert = options?.revert;
this.silent = options?.silent;
}
};
function isCancelledError(value) {
return value instanceof CancelledError;
}
function createRetryer(config) {
let isRetryCancelled = false;
let failureCount = 0;
let isResolved = false;
let continueFn;
const thenable = pendingThenable();
const cancel = (cancelOptions) => {
if (!isResolved) {
reject(new CancelledError(cancelOptions));
config.abort?.();
}
};
const cancelRetry = () => {
isRetryCancelled = true;
};
const continueRetry = () => {
isRetryCancelled = false;
};
const canContinue = () => focusManager.isFocused() && (config.networkMode === "always" || onlineManager.isOnline()) && config.canRun();
const canStart = () => canFetch(config.networkMode) && config.canRun();
const resolve = (value) => {
if (!isResolved) {
isResolved = true;
config.onSuccess?.(value);
continueFn?.();
thenable.resolve(value);
}
};
const reject = (value) => {
if (!isResolved) {
isResolved = true;
config.onError?.(value);
continueFn?.();
thenable.reject(value);
}
};
const pause = () => {
return new Promise((continueResolve) => {
continueFn = (value) => {
if (isResolved || canContinue()) {
continueResolve(value);
}
};
config.onPause?.();
}).then(() => {
continueFn = void 0;
if (!isResolved) {
config.onContinue?.();
}
});
};
const run = () => {
if (isResolved) {
return;
}
let promiseOrValue;
const initialPromise = failureCount === 0 ? config.initialPromise : void 0;
try {
promiseOrValue = initialPromise ?? config.fn();
} catch (error) {
promiseOrValue = Promise.reject(error);
}
Promise.resolve(promiseOrValue).then(resolve).catch((error) => {
if (isResolved) {
return;
}
const retry = config.retry ?? (isServer ? 0 : 3);
const retryDelay = config.retryDelay ?? defaultRetryDelay;
const delay = typeof retryDelay === "function" ? retryDelay(failureCount, error) : retryDelay;
const shouldRetry = retry === true || typeof retry === "number" && failureCount < retry || typeof retry === "function" && retry(failureCount, error);
if (isRetryCancelled || !shouldRetry) {
reject(error);
return;
}
failureCount++;
config.onFail?.(failureCount, error);
sleep(delay).then(() => {
return canContinue() ? void 0 : pause();
}).then(() => {
if (isRetryCancelled) {
reject(error);
} else {
run();
}
});
});
};
return {
promise: thenable,
cancel,
continue: () => {
continueFn?.();
return thenable;
},
cancelRetry,
continueRetry,
canStart,
start: () => {
if (canStart()) {
run();
} else {
pause().then(run);
}
return thenable;
}
};
}
export {
CancelledError,
canFetch,
createRetryer,
isCancelledError
};
//# sourceMappingURL=retryer.js.map