Refactor Sync Function with Schedule for Retry Until Predicate Passes

I think I've worked myself into a confusion. I wrote this helper to run a sync function on a schedule, with some delay between runs, until a predicate passes, or it times out after some given max duration

I feel like there has to be a more idiomatic way to write this with Schedule that I'm just not seeing. I'm probably over-thinking it

Bonus if the try can be an Effect itself

export const tryUntil = <A, B extends A>({
  try: doTry,
  until: isDone,
  sleepDuration = Duration.millis(200),
  maxDuration,
}: {
  try: () => A;
  until: Predicate.Refinement<A, B>;
  sleepDuration?: Duration.Duration;
  maxDuration: Duration.Duration;
}): Effect.Effect<B, Error, never> => {
  const immediateValue = doTry();
  if (isDone(immediateValue)) {
    return Effect.succeed(immediateValue);
  }

  // Continually call it again on a schedule with a delay
  return Effect.sync(doTry).pipe(
    // Sleep in between each attempt
    Effect.delay(sleepDuration),

    // Keep doing this until the predicate passes
    Effect.repeat({ until: isDone }),

    // Until a timeout occurs
    Effect.timeoutFail({
      onTimeout: () =>
        new Error(
          `Timed out after ${Duration.format(maxDuration)} waiting for value to pass predicate`,
        ),
      duration: maxDuration,
    }),
  );
};
Was this page helpful?