Skip to content
asyncredux-retry-mixin logo

Retry Mixin

asyncredux-retry-mixin

Add the Retry mixin for automatic retry with exponential backoff on action failure. Covers using Retry alone for limited retries, combining with UnlimitedRetries for infinite retries, and configuring retry behavior.

SKILL.md

Full skill instructions

Retry Mixin

The Retry mixin automatically retries failed actions using exponential backoff. When an error occurs in the reduce() method, the action is re-executed with progressively increasing delays between attempts.

Basic Usage

Add the Retry mixin to any action that should automatically retry on failure:

class LoadDataAction extends AppAction with Retry {
  Future<AppState?> reduce() async {
    var data = await fetchDataFromServer();
    return state.copy(data: data);
  }
}

With this mixin:

  • If the action fails, it automatically retries up to 3 times (default)
  • Each retry waits longer than the previous (exponential backoff)
  • If all retries fail, the original error is thrown

Configuration Parameters

Override these getters to customize retry behavior:

class LoadDataAction extends AppAction with Retry {

  // Delay before first retry (default: 350ms)
  int get initialDelay => 500;

  // Multiplier for delay growth (default: 2)
  int get multiplier => 2;

  // Maximum retry attempts (default: 3)
  int get maxRetries => 5;

  // Upper limit on delay to prevent excessive waits (default: 5000ms)
  int get maxDelay => 10000;

  Future<AppState?> reduce() async {
    var data = await fetchDataFromServer();
    return state.copy(data: data);
  }
}
ParameterDefaultPurpose
initialDelay350 msWaiting period before first retry
multiplier2Growth factor for delays between attempts
maxRetries3Maximum retry count (total executions = maxRetries + 1)
maxDelay5 secUpper limit on delay to prevent excessive waits

Retry Sequence Example

With default settings (initialDelay=350ms, multiplier=2, maxRetries=3):

  1. Initial attempt - Action runs, fails
  2. Wait 350ms - First retry, fails
  3. Wait 700ms - Second retry, fails
  4. Wait 1400ms - Third retry, fails
  5. Error thrown - All retries exhausted

Timing Considerations

Retry delays start after the reducer finishes, not from when the action was dispatched. If reduce() takes 1 second to fail and initialDelay is 350ms, the first retry starts 1.35 seconds after the action began.

Tracking Retry Attempts

Access the attempts getter within your action to know which attempt is currently running:

class LoadDataAction extends AppAction with Retry {
  Future<AppState?> reduce() async {
    print('Attempt ${attempts + 1}'); // 0-indexed, so first attempt is 0

    if (attempts > 0) {
      // Maybe try a different server on retries
      return state.copy(data: await fetchFromBackupServer());
    }

    return state.copy(data: await fetchFromPrimaryServer());
  }
}

Unlimited Retries

Combine UnlimitedRetries with Retry to retry indefinitely until the action succeeds:

class CriticalSyncAction extends AppAction with Retry, UnlimitedRetries {
  Future<AppState?> reduce() async {
    await syncCriticalData();
    return state.copy(syncComplete: true);
  }
}

This is equivalent to setting maxRetries to -1.

Warning: Using await dispatchAndWait(action) with UnlimitedRetries may hang indefinitely if the action continues failing. Use with caution and consider whether the action has a realistic chance of eventually succeeding.

Important Behavior Notes

Only reduce() Failures Trigger Retry

The Retry mixin only retries when errors occur in the reduce() method. Failures in the before() method do not trigger retries - they fail immediately.

class LoadDataAction extends AppAction with Retry {

  @override
  Future<void> before() async {
    // Errors here will NOT trigger retry - action fails immediately
    await validatePermissions();
  }

  Future<AppState?> reduce() async {
    // Only errors here trigger the retry mechanism
    return state.copy(data: await fetchData());
  }
}

Actions Become Asynchronous

All actions using the Retry mixin become asynchronous, regardless of their original synchronous nature. This is because the retry mechanism needs to wait between attempts.

Combining with NonReentrant (Best Practice)

Most actions using Retry should also include the NonReentrant mixin to prevent multiple instances from running simultaneously:

class SaveDataAction extends AppAction with NonReentrant, Retry {
  Future<AppState?> reduce() async {
    await saveToServer();
    return state.copy(saved: true);
  }
}

This prevents scenarios where:

  • User clicks "Save" multiple times
  • Multiple retry sequences run in parallel
  • Server receives duplicate or conflicting requests

Combining with CheckInternet

For network operations, combine Retry with CheckInternet to ensure connectivity before attempting the action:

class FetchUserProfile extends AppAction with CheckInternet, Retry {
  Future<AppState?> reduce() async {
    var profile = await api.getUserProfile();
    return state.copy(profile: profile);
  }
}

The CheckInternet mixin runs first. If there's no connection, the action fails immediately without attempting retries.

Common Use Cases

API Calls with Transient Failures

class FetchProductsAction extends AppAction with Retry {
  int get maxRetries => 3;
  int get initialDelay => 500;

  Future<AppState?> reduce() async {
    var products = await api.getProducts();
    return state.copy(products: products);
  }
}

Critical Sync Operations

class SyncPendingChanges extends AppAction with Retry, UnlimitedRetries {
  int get initialDelay => 1000;
  int get maxDelay => 30000; // Cap at 30 seconds between retries

  Future<AppState?> reduce() async {
    await syncService.pushPendingChanges();
    return state.copy(hasPendingChanges: false);
  }
}

Payment Processing with Extended Retries

class ProcessPaymentAction extends AppAction with NonReentrant, Retry {
  final double amount;

  ProcessPaymentAction(this.amount);

  int get maxRetries => 5;
  int get initialDelay => 1000;
  int get multiplier => 2;
  int get maxDelay => 10000;

  Future<AppState?> reduce() async {
    var result = await paymentGateway.process(amount);
    return state.copy(paymentStatus: result.status);
  }
}

Mixin Compatibility

Compatible with:

  • CheckInternet
  • NoDialog
  • AbortWhenNoInternet
  • NonReentrant
  • Throttle
  • Debounce

Can be combined with:

  • UnlimitedRetries (enables infinite retries)

Full Example with All Options

class RobustApiAction extends AppAction
    with CheckInternet, NonReentrant, Retry {

  // Retry configuration
  int get initialDelay => 500;     // 500ms before first retry
  int get multiplier => 2;          // Double delay each time
  int get maxRetries => 4;          // Try up to 5 times total
  int get maxDelay => 8000;         // Never wait more than 8 seconds

  Future<AppState?> reduce() async {
    if (attempts > 0) {
      print('Retry attempt $attempts');
    }

    var data = await api.fetchCriticalData();
    return state.copy(data: data);
  }
}

References

URLs from the documentation:

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View