From 6b3a0599119d8f46f4a8d93c39d88f9cee09f0f9 Mon Sep 17 00:00:00 2001 From: Steven Wexler Date: Sun, 1 Dec 2024 23:57:42 -0700 Subject: [PATCH] Add a blog --- docs/.vitepress/config.mts | 17 +++++- docs/blog/delayingExecutionWithWait.md | 21 +++++++ docs/blog/implementingRetryLogic.md | 77 ++++++++++++++++++++++++++ docs/blog/index.md | 54 ++++++++++++++++++ docs/examples.md | 2 +- 5 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 docs/blog/delayingExecutionWithWait.md create mode 100644 docs/blog/implementingRetryLogic.md create mode 100644 docs/blog/index.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 5faac98..5f50285 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -23,9 +23,9 @@ export default defineConfig({ // https://vitepress.dev/reference/default-theme-config nav: [ { text: "Home", link: "/" }, - { text: "Getting Started", link: "/gettingStarted" }, { text: "Guide", link: "/query" }, - { text: "Examples", link: "/examples" } + { text: "Examples", link: "/examples" }, + { text: "Blog", link: "/blog" } ], sidebar: [ { @@ -55,7 +55,18 @@ export default defineConfig({ { text: "Examples", items: [ - { text: "Examples", link: "/examples" }, + { text: "All Examples", link: "/examples" }, + { text: "Bears JS", link: "https://codesandbox.io/p/sandbox/leo-query-bears-demo-js-wmwlgt?file=%2Fsrc%2FApp.jsx" }, + { text: "Bears TS", link: "https://codesandbox.io/p/sandbox/leo-query-bears-demo-ts-7f2c34?file=%2Fsrc%2FApp.tsx" }, + { text: "Task Manager", link: "https://xsh8c4.csb.app/" }, + ] + }, + { + text: "Blog", + items: [ + { text: "Recent Posts", link: "/blog" }, + { text: "Implementing Retry Logic", link: "/blog/implementingRetryLogic" }, + { text: "Delaying Execution with Wait", link: "/blog/delayingExecutionWithWait" }, ] } ], diff --git a/docs/blog/delayingExecutionWithWait.md b/docs/blog/delayingExecutionWithWait.md new file mode 100644 index 0000000..25b7fbf --- /dev/null +++ b/docs/blog/delayingExecutionWithWait.md @@ -0,0 +1,21 @@ +# Delaying Execution with Wait + +Sometimes I want to delay execution in Leo Query. This may be to delay a retry or wait for a React render. I prefer the async / await syntax over `setTimeout`. So I wrote a small `wait` utility function to use in the Leo Query code. + +```typescript +const wait = async (timeout?: number) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(null); + }, timeout); + }); +}; + +const myFunction = async () => { + //Do something + await wait(5 * 1000); //Wait 5 seconds + //Do more things +}; +``` + +Happy Coding! \ No newline at end of file diff --git a/docs/blog/implementingRetryLogic.md b/docs/blog/implementingRetryLogic.md new file mode 100644 index 0000000..7ff78f5 --- /dev/null +++ b/docs/blog/implementingRetryLogic.md @@ -0,0 +1,77 @@ +# Implementing Retry Logic + +Lots of things can go wrong when you're making HTTP requests. Wifi can cut out. Servers can overload. Apps need to have a retry strategy for good UX. Leo Query implements a nuanced retry strategy. + +When building a retry strategy you need to answer three questions. + +### When should you start retrying? + +You should retry as soon as possible. Network or server errors often resolve quickly. + +```typescript +const trigger = () => { + const promise = fetch(); + promise.catch(() => retry(p.fn, q, promise)); //If the promise fails immediately retry +}; +``` + +### How long should you wait between each retry? + +Three common strategies for waiting are constant backoff, linear backoff, and exponential backoff. + +Constant backoff waits the same amount of time between retries. This strategy is good for keeping your system simple and retrying quickly. + +Linear backoff waits a linearly increasing amount between each retry. Linear backoffs are good for still keeping your system simple but giving the issue more time to resolve. + +Exponential backoff waits exponentially more time between each retry. Exponential backoffs work well because they retry quickly initially and then give issue more time to resolve in later retries. This pattern works well because most issues resolve quickly, but issues that do not tend to take significantly longer. Leo Query uses a modified exponential backoff. + +```typescript +const calculateBackoffDelay = (attempt: number) => + attempt === 0 ? 0 : Math.min((2 ** (attempt - 1)) * 1000, 30 * 1000); +``` + +### When should you stop retrying? + +You should stop retrying when you no longer need the data or when there's no hope left to retrieve the data. Leo Query keeps track of when the data is no longer needed. And by default it gives up after 5 retries. + +## Implementation + +Putting it all together this is what the [retry](https://github.com/steaks/leo-query/blob/main/src/retry.ts) code looks like. + +```typescript +const wait = async (timeout?: number) => { + return new Promise((resolve) => { + setTimeout(() => { + resolve(null); + }, timeout); + }); +}; + +export const calculateBackoffDelay = (attempt: number) => + attempt === 0 ? 0 : Math.min((2 ** (attempt - 1)) * 1000, 30 * 1000); + +export const retry = async (fn: () => Promise, query: Query, promise: Promise, attempt: number = 0): Promise => { + try { + return await fn(); + } catch (error) { + if (attempt >= query.__retries) { //Defaults to 5 retries + throw error; + } + const state = query.__store().getState(); + const current = state[query.__key] as Query; + if (current.__trigger !== promise) { //If the query has been re-triggered then we no longer need the data + throw error; + } + const backoffDelay = calculateBackoffDelay(attempt); //Use exponential backoff + await wait(backoffDelay); + return retry(fn, query, promise, attempt + 1); + } +}; + +const trigger = () => { + const promise = fetch(); + promise.catch(() => retry(p.fn, q, promise)); //If the promise fails then we immediately retry +}; +``` + +Happy Coding! \ No newline at end of file diff --git a/docs/blog/index.md b/docs/blog/index.md new file mode 100644 index 0000000..f6cb457 --- /dev/null +++ b/docs/blog/index.md @@ -0,0 +1,54 @@ + + +# Blog + +Welcome to the Leo Query blog! Here you'll find articles about state management, data fetching, and more. + +## Latest Posts + +
+

Implementing Retry Logic

+ +

+ Lots of things can go wrong when you're making HTTP requests. Wifi can cut out. Servers can overload. Apps need to have a retry strategy for good UX. Leo Query implements a nuanced retry strategy..... + read more +

+
+ +
+

Delaying Execution with Wait

+ +

+ Sometimes I want to delay execution in Leo Query. This may be to delay a retry or wait for a React render. I prefer the async / await syntax over `setTimeout`. So I wrote a small `wait` utility function to use in the Leo Query code..... + read more +

+
\ No newline at end of file diff --git a/docs/examples.md b/docs/examples.md index a730d91..70d55f3 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -1,6 +1,6 @@ # Examples -Here are some live examples to see leo-query. +Here are live examples to see Leo Query in action. | Example | Description | |---------------------------------------------------------------------------------------------------|------------------------------------|