Specify a webhook-driven task for OpenClaw
Describe event inputs, validation, deduplication, and intended actions before integration work.
5,185,335 AI resources indexedWorking toward the world’s largest AI resource index.
Async programming patterns with UniTask, cancellation tokens, and exception handling. Use when writing async code, handling CancellationTokenSource lifecycle, using SuppressToResultAsync, implementing detached UniTask/UniTaskVoid flows, or working with Result/EnumResult types for exception-free f...
---
name: async-programming-276fce014735
description: "Async programming patterns with UniTask, cancellation tokens, and exception handling. Use when writing async code, handling CancellationTokenSource lifecycle, using SuppressToResultAsync, implementing detached UniTask/UniTaskVoid flows, or working with Result/EnumResult types for exception-free flow propagation."
user-invocable: false
---
# Async Programming Patterns
## Sources
- `docs/async-programming.md` — Async pattern guidelines for UniTask flows
- `docs/architecture-overview.md` — Result struct and exception-free flow details
---
## Core Rules
> Condensed rules are in CLAUDE.md §9. This skill provides expanded patterns and code examples not covered there.
## Exception Handling Pattern
### Wrong — Unhandled exceptions in detached flow
```csharp
// BAD: Exception kills the flow silently
async UniTaskVoid DoSomethingAsync(CancellationToken ct)
{
var result = await webRequestController.GetAsync(args, ct);
// If this throws, nobody catches it
}
```
### Correct — Proper exception handling
```csharp
async UniTaskVoid DoSomethingAsync(CancellationToken ct)
{
try
{
var result = await webRequestController.GetAsync(args, ct);
ProcessResult(result);
}
catch (OperationCanceledException) { } // Normal cancellation — ignore
catch (Exception exception)
{
ReportHub.LogException(exception, ReportCategory.GENERIC_WEB_REQUEST);
}
}
```
### Code Example — Exception Handling in Controller
From `MinimapController.cs`:
```csharp
private async UniTask<PlacesData.PlaceInfo?> GetPlaceInfoAsync(
Vector2Int parcelPosition, CancellationToken ct, bool renewCache = false)
{
try
{
return await placesAPIService.GetPlaceAsync(parcelPosition, ct, renewCache);
}
catch (OperationCanceledException _) { }
catch (NotAPlaceException notAPlaceException)
{
ReportHub.LogWarning(ReportCategory.UNSPECIFIED,
$"Not a place requested: {notAPlaceException.Message}");
}
catch (Exception exception)
{
ReportHub.LogException(exception, ReportCategory.GENERIC_WEB_REQUEST);
}
return null;
}
```
## SuppressToResultAsync Pattern
Wraps a `UniTask<T>` in a try/catch and returns a `Result<T>` struct instead of throwing.
### Code Example — SuppressToResultAsync + Result
From `TokenFileAuthenticator.cs`:
```csharp
private async UniTask<IWeb3Identity> LoginAsync(CancellationToken ct)
{
if (!File.Exists(TOKEN_PATH))
throw new AutoLoginTokenNotFoundException();
// SuppressToResultAsync catches exceptions and wraps in Result<string>
Result<string> contentResult = await File.ReadAllTextAsync(TOKEN_PATH, ct)!
.SuppressToResultAsync<string>(ReportCategory.AUTHENTICATION);
if (contentResult.Success == false)
throw new Exception(contentResult.ErrorMessage ?? "Cannot read token file");
string token = contentResult.Value;
// ... continue with token ...
}
```
## Result and EnumResult Types
### Result Struct
Zero-cost value type for exception-free flow propagation:
```csharp
// Success
return Result<string>.SuccessResult(value);
// Error
return Result<string>.ErrorResult("Something went wrong");
// Check
if (result.Success)
UseValue(result.Value);
else
HandleError(result.ErrorMessage);
```
### REnum Union Types
Roslyn Source Generator for Rust-style enums. Zero-cost pattern matching:
```csharp
// Pattern matching via .Match()
result.Match(
success: value => ProcessValue(value),
error: msg => LogError(msg)
);
// Safe access via .IsXXX(out T)
if (result.IsSuccess(out var value))
ProcessValue(value);
```
## Cancellation Token Management
### CancellationTokenSource Lifecycle
From `MinimapController.cs` — proper CTS management:
```csharp
private CancellationTokenSource? placesApiCts;
protected override void OnFocus()
{
// Cancel any in-flight request
placesApiCts.SafeCancelAndDispose();
placesApiCts = new CancellationTokenSource();
RefreshPlaceInfoUIAsync(previousParcelPosition, placesApiCts.Token).Forget();
}
// SafeRestart = cancel + dispose + create new
private void OnFavoriteButtonClicked(bool value)
{
favoriteCancellationToken = favoriteCancellationToken.SafeRestart();
SetAsFavoriteAsync(favoriteCancellationToken.Token).Forget();
}
public override void Dispose()
{
placesApiCts.SafeCancelAndDispose();
disposeCts.Cancel();
favoriteCancellationToken.SafeCancelAndDispose();
}
```
**Key patterns:**
- `SafeCancelAndDispose()` — Cancel and dispose in one call, null-safe
- `SafeRestart()` — Cancel + dispose + create new CTS
- Always dispose CTS in `Dispose()`
- Use a dedicated `disposeCts` for operations that should cancel on controller disposal
### Cancellation Checking
```csharp
// CORRECT — Check without throwing (cheap)
if (ct.IsCancellationRequested)
return;
// WRONG — Throws exception (expensive)
ct.ThrowIfCancellationRequested();
```
---
name: async-programming-276fce014735
description: "Async programming patterns with UniTask, cancellation tokens, and exception handling. Use when writing async code, handling CancellationTokenSource lifecycle, using SuppressToResultAsync, implementing detached UniTask/UniTaskVoid flows, or working with Result/EnumResult types for exception-free flow propagation."
user-invocable: false
---
# Async Programming Patterns
## Sources
- `docs/async-programming.md` — Async pattern guidelines for UniTask flows
- `docs/architecture-overview.md` — Result struct and exception-free flow details
---
## Core Rules
> Condensed rules are in CLAUDE.md §9. This skill provides expanded patterns and code examples not covered there.
## Exception Handling Pattern
### Wrong — Unhandled exceptions in detached flow
```csharp
// BAD: Exception kills the flow silently
async UniTaskVoid DoSomethingAsync(CancellationToken ct)
{
var result = await webRequestController.GetAsync(args, ct);
// If this throws, nobody catches it
}
```
### Correct — Proper exception handling
```csharp
async UniTaskVoid DoSomethingAsync(CancellationToken ct)
{
try
{
var result = await webRequestController.GetAsync(args, ct);
ProcessResult(result);
}
catch (OperationCanceledException) { } // Normal cancellation — ignore
catch (Exception exception)
{
ReportHub.LogException(exception, ReportCategory.GENERIC_WEB_REQUEST);
}
}
```
### Code Example — Exception Handling in Controller
From `MinimapController.cs`:
```csharp
private async UniTask<PlacesData.PlaceInfo?> GetPlaceInfoAsync(
Vector2Int parcelPosition, CancellationToken ct, bool renewCache = false)
{
try
{
return await placesAPIService.GetPlaceAsync(parcelPosition, ct, renewCache);
}
catch (OperationCanceledException _) { }
catch (NotAPlaceException notAPlaceException)
{
ReportHub.LogWarning(ReportCategory.UNSPECIFIED,
$"Not a place requested: {notAPlaceException.Message}");
}
catch (Exception exception)
{
ReportHub.LogException(exception, ReportCategory.GENERIC_WEB_REQUEST);
}
return null;
}
```
## SuppressToResultAsync Pattern
Wraps a `UniTask<T>` in a try/catch and returns a `Result<T>` struct instead of throwing.
### Code Example — SuppressToResultAsync + Result
From `TokenFileAuthenticator.cs`:
```csharp
private async UniTask<IWeb3Identity> LoginAsync(CancellationToken ct)
{
if (!File.Exists(TOKEN_PATH))
throw new AutoLoginTokenNotFoundException();
// SuppressToResultAsync catches exceptions and wraps in Result<string>
Result<string> contentResult = await File.ReadAllTextAsync(TOKEN_PATH, ct)!
.SuppressToResultAsync<string>(ReportCategory.AUTHENTICATION);
if (contentResult.Success == false)
throw new Exception(contentResult.ErrorMessage ?? "Cannot read token file");
string token = contentResult.Value;
// ... continue with token ...
}
```
## Result and EnumResult Types
### Result Struct
Zero-cost value type for exception-free flow propagation:
```csharp
// Success
return Result<string>.SuccessResult(value);
// Error
return Result<string>.ErrorResult("Something went wrong");
// Check
if (result.Success)
UseValue(result.Value);
else
HandleError(result.ErrorMessage);
```
### REnum Union Types
Roslyn Source Generator for Rust-style enums. Zero-cost pattern matching:
```csharp
// Pattern matching via .Match()
result.Match(
success: value => ProcessValue(value),
error: msg => LogError(msg)
);
// Safe access via .IsXXX(out T)
if (result.IsSuccess(out var value))
ProcessValue(value);
```
## Cancellation Token Management
### CancellationTokenSource Lifecycle
From `MinimapController.cs` — proper CTS management:
```csharp
private CancellationTokenSource? placesApiCts;
protected override void OnFocus()
{
// Cancel any in-flight request
placesApiCts.SafeCancelAndDispose();
placesApiCts = new CancellationTokenSource();
RefreshPlaceInfoUIAsync(previousParcelPosition, placesApiCts.Token).Forget();
}
// SafeRestart = cancel + dispose + create new
private void OnFavoriteButtonClicked(bool value)
{
favoriteCancellationToken = favoriteCancellationToken.SafeRestart();
SetAsFavoriteAsync(favoriteCancellationToken.Token).Forget();
}
public override void Dispose()
{
placesApiCts.SafeCancelAndDispose();
disposeCts.Cancel();
favoriteCancellationToken.SafeCancelAndDispose();
}
```
**Key patterns:**
- `SafeCancelAndDispose()` — Cancel and dispose in one call, null-safe
- `SafeRestart()` — Cancel + dispose + create new CTS
- Always dispose CTS in `Dispose()`
- Use a dedicated `disposeCts` for operations that should cancel on controller disposal
### Cancellation Checking
```csharp
// CORRECT — Check without throwing (cheap)
if (ct.IsCancellationRequested)
return;
// WRONG — Throws exception (expensive)
ct.ThrowIfCancellationRequested();
```
Imported from the listed AgentSkill.sh category. Complete supplied package files and notices are retained. Required external tools and credentials are not installed. No bundled code was executed during import. Individual agent task behavior has not been tested.