Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create API for retrying throwed functions and rejected promises #96

Open
parzhitsky opened this issue Jun 19, 2021 · 5 comments
Open

Create API for retrying throwed functions and rejected promises #96

parzhitsky opened this issue Jun 19, 2021 · 5 comments
Labels
Change: minor [Issue / PR] describes a non-breaking change, such as adding a new functionality Domain: main [Issue / PR] describes change in the functionality, its optimization Pending: blocked [Issue / PR] cannot be addressed until another issue is resolved Pending: unclear [Issue] not yet fully defined Priority: medium [Issue / PR] should be addressed without delay Type: improvement [Issue / PR] addresses lack of a functionality or an open possibility of enhancement

Comments

@parzhitsky
Copy link
Member

parzhitsky commented Jun 19, 2021

Related: #62 #76

Think about something like:

// `fn` is a function that either: returns a non-promise result; returns a pending/resolved/rejected promise; throws error;
const result = await retry.action(fn, {
  promiseTimeout: 1000,
  retryDelay: 2000,
  maxCount: 10,
  onMaxCountExceeded: (resolve, reject, retry) => {
    // ...
  },
});
declare namespace retry {
  function action<Result>(fn: (...args: never[]) => Result | PromiseLike<Result>), params?: RetryActionParams): Promise<Result>;
}
interface RetryActionParams {
  /**
   * If the function returns a pending promise, and it is not fulfilled after this amount of milliseconds, – fail the attempt
   */
  promiseTimeout?: number;

  /**
   * Value for `retry.after(...)`
   *
   * Note:
   * This being `undefined` (or omitted) will produce recursive retries (i.e., call stack overflow is possible)
   * To "pop" no-delay calls from the call stack, either set this to `0` (to invoke `retry.after(0)`),
   * or set `forceAsync` param to `true` (see below)
   */
  retryDelay?: Delay;

  /**
   * Low-level param that governs how to treat no-delay retries (i.e., if `retryDelay` is not present or is `0`).
   * If this is `true`, then no-delay retries will always invoke `retry.after(0)`,
   * which delegates to `setTimeout` internally, thus making the call asynchronous.
   * If this is `false`, then no particular logic is added (see description of `retryDelay`).
   *
   * Note:
   * If `retryDelay` is set to a non-zero value, this param is ignored (delayed retries are always asynchronous)
   *
   * @default false
   */
  forceAsync?: boolean;

  /**
   * Value for the first argument of `retry.setMaxCount(...)`
   */
  maxCount?: number;

  /**
   * Value for the second argument of `retry.setMaxCount(...)`
   */
  onMaxCountExceeded?: OnMaxRetryCountExceeded;
}
@parzhitsky parzhitsky added Change: minor [Issue / PR] describes a non-breaking change, such as adding a new functionality Domain: main [Issue / PR] describes change in the functionality, its optimization Pending: unclear [Issue] not yet fully defined Priority: medium [Issue / PR] should be addressed without delay Type: improvement [Issue / PR] addresses lack of a functionality or an open possibility of enhancement labels Jun 19, 2021
@parzhitsky
Copy link
Member Author

Okay, but how do you retry promises without providing a promise-generating function?

@parzhitsky
Copy link
Member Author

parzhitsky commented Jun 21, 2021

Right: the argument is always a function. Possible outcomes are:

  • ❌ it returns a rejected promise, or throws, which starts the retrying (according to the provided params);
  • ✅ it returns a non-promise value or a resolved promise, which is considered to be the result;
  • ⚠️ it returns a pending promise, that:
    • ❌ is timed out or rejected, which starts the retrying (according to the provided params);
    • ✅ is resolved with a value, that is considered to be the result;

@parzhitsky
Copy link
Member Author

parzhitsky commented Jun 21, 2021

Also, think about useful defaults. For perfect configurability, it would make sense to have defaults set to:

retry.action(funcOrProm, {
  promiseTimeout: Infinity,
  retryDelay: undefined,
  forceAsync: false,
  maxCount: Infinity,
  onMaxCountExceeded(resolve, reject, retry) {
    reject(`Action failed after ${retry.count + 1} attempts`); // or some message like this
  },
});

Practically, however, it would be nice to be able to just wrap the thing in retry.action(), as in:

const result = await retry.action(db.connect);
// makes several attempts asynchronously,
// then fails with retry count exceeded error

… which requires different defaults.

@parzhitsky parzhitsky changed the title Create API for retrying throwing functions and rejecting promises Create API for retrying throwed functions and rejected promises Jun 21, 2021
@parzhitsky
Copy link
Member Author

Interesting: currently, the retry() function accepts an action parameter, which is a function with promise-constructor-executor-like signature:

// roughly
type Action = <Result>(resolve: Function, reject: Function, retry: Retrier) => Promise<Result>;
// roughly
type Executor = <Result>(resolve: Function, reject: Function) => Promise<Result>;

This means, that it would be backward compatible to provide those params as the second argument of retry(), as in:

const result = await retry(db.connect, { retryDelay: ... });

… which frankly should've been the first choice of the function signature.

@parzhitsky parzhitsky added the Pending: blocked [Issue / PR] cannot be addressed until another issue is resolved label Jun 21, 2021
@parzhitsky
Copy link
Member Author

Blocked by #97

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Change: minor [Issue / PR] describes a non-breaking change, such as adding a new functionality Domain: main [Issue / PR] describes change in the functionality, its optimization Pending: blocked [Issue / PR] cannot be addressed until another issue is resolved Pending: unclear [Issue] not yet fully defined Priority: medium [Issue / PR] should be addressed without delay Type: improvement [Issue / PR] addresses lack of a functionality or an open possibility of enhancement
Projects
None yet
Development

No branches or pull requests

1 participant