Skip to content

Examples

Meyn edited this page Dec 15, 2025 · 1 revision

$\text{\color{green}Usage Examples}$

Comprehensive examples demonstrating real-world usage of $\text{\color{gray}Shard.\color{orange}Requests}$ for common scenarios.

Table of Contents


$\text{\color{lightblue}Basic Examples}$

Simple Request with OwnRequest

using Shard.Requests;

// Minimal setup - auto-starts and uses main handler
var request = new OwnRequest(async token =>
{
    await Task.Delay(1000, token);
    Console.WriteLine("Task completed!");
    return true; // Success
});

// Wait for completion
await request.Task;

Request with Configuration

var request = new OwnRequest(async token =>
{
    // Simulate operation that might fail
    if (Random.Shared.Next(0, 3) == 0)
        return false; // Trigger retry

    await Task.Delay(500, token);
    return true;
}, new RequestOptions<VoidStruct, VoidStruct>
{
    Priority = RequestPriority.High,
    NumberOfAttempts = 5,
    DelayBetweenAttempts = TimeSpan.FromSeconds(2),
    RequestStarted = req => Console.WriteLine("Starting..."),
    RequestCompleted = (req, _) => Console.WriteLine("Success!"),
    RequestFailed = (req, _) => Console.WriteLine("Failed after all retries"),
    RequestExceptionOccurred = (req, ex) => Console.WriteLine($"Error: {ex.Message}")
});

await request.Task;

Manual State Control

var request = new OwnRequest(async token =>
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(100, token);
        Console.WriteLine($"Step {i + 1}/10");
    }
    return true;
}, new() { AutoStart = false }); // Don't auto-start

// Start manually
Console.WriteLine("Press Enter to start...");
Console.ReadLine();
request.Start();

// Pause
Console.WriteLine("Press Enter to pause...");
Console.ReadLine();
request.Pause();

// Resume
Console.WriteLine("Press Enter to resume...");
Console.ReadLine();
request.Start();

await request.Task;

$\text{\color{orange}File Operations}$

Batch File Download

using Shard.Requests;
using System.Net.Http;

public class FileDownloader
{
    private readonly HttpClient _client = new();
    private readonly ParallelRequestHandler _handler = new()
    {
        StaticDegreeOfParallelism = 5 // 5 concurrent downloads
    };

    public async Task DownloadFilesAsync(Dictionary<string, string> urlToPath)
    {
        var requests = urlToPath.Select(kvp => new OwnRequest(async token =>
        {
            try
            {
                var response = await _client.GetAsync(kvp.Key, token);
                response.EnsureSuccessStatusCode();

                var content = await response.Content.ReadAsByteArrayAsync(token);
                await File.WriteAllBytesAsync(kvp.Value, content, token);

                Console.WriteLine($"Downloaded: {kvp.Key}");
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Failed {kvp.Key}: {ex.Message}");
                return false; // Retry
            }
        }, new()
        {
            Handler = _handler,
            NumberOfAttempts = 3,
            DelayBetweenAttempts = TimeSpan.FromSeconds(5)
        })).ToList();

        // Wait for all downloads
        await _handler.Task;
    }
}

// Usage:
var downloader = new FileDownloader();
await downloader.DownloadFilesAsync(new Dictionary<string, string>
{
    ["https://example.com/file1.zip"] = "file1.zip",
    ["https://example.com/file2.zip"] = "file2.zip",
    ["https://example.com/file3.zip"] = "file3.zip"
});

Directory Search with Progress

public class DirectoryScanner
{
    public async Task<List<string>> FindFilesAsync(
        string rootPath,
        string pattern,
        IProgress<float> progress)
    {
        var results = new List<string>();
        var directories = Directory.GetDirectories(rootPath, "*", SearchOption.AllDirectories);
        int processed = 0;

        var request = new OwnRequest(async token =>
        {
            foreach (var dir in directories)
            {
                token.ThrowIfCancellationRequested();
                await Request.Yield(); // Allow pause/cancel

                var files = Directory.GetFiles(dir, pattern);
                lock (results)
                {
                    results.AddRange(files);
                }

                processed++;
                progress.Report((float)processed / directories.Length);
            }

            return true;
        });

        await request.Task;
        return results;
    }
}

// Usage:
var scanner = new DirectoryScanner();
var foundFiles = await scanner.FindFilesAsync(
    "C:\\Data",
    "*.log",
    new Progress<float>(p => Console.WriteLine($"Scanning: {p:P}"))
);

Console.WriteLine($"Found {foundFiles.Count} files");

File Processing Pipeline

public async Task ProcessFilesAsync(string[] filePaths)
{
    foreach (var filePath in filePaths)
    {
        // Create pipeline: Read → Validate → Process → Save
        var readRequest = new OwnRequest(async token =>
        {
            var data = await File.ReadAllTextAsync(filePath, token);
            return !string.IsNullOrEmpty(data);
        });

        var validateRequest = new OwnRequest(async token =>
        {
            // Validation logic
            await Task.Delay(100, token);
            return true;
        });

        var processRequest = new OwnRequest(async token =>
        {
            // Processing logic
            await Task.Delay(500, token);
            return true;
        });

        var saveRequest = new OwnRequest(async token =>
        {
            await File.WriteAllTextAsync($"{filePath}.processed", "result", token);
            return true;
        });

        // Chain them
        readRequest.Options.SubsequentRequest = validateRequest;
        validateRequest.Options.SubsequentRequest = processRequest;
        processRequest.Options.SubsequentRequest = saveRequest;

        // Start only the first - rest follow automatically
        readRequest.Start();
    }

    await ParallelRequestHandler.MainRequestHandler.Task;
}

$\text{\color{blue}HTTP Requests}$

API Rate Limiting

public class RateLimitedApiClient
{
    private readonly HttpClient _client = new();
    private readonly ParallelRequestHandler _handler;
    private int _remainingRequests = 100;
    private readonly SemaphoreSlim _rateLimiter = new(10, 10);

    public RateLimitedApiClient()
    {
        _handler = new ParallelRequestHandler
        {
            AutoParallelism = () =>
            {
                // Adjust concurrency based on remaining quota
                if (_remainingRequests < 10)
                    return 1; // Throttle down
                else if (_remainingRequests < 50)
                    return 3;
                else
                    return 10;
            },
            MaxParallelism = 10
        };

        // Reset rate limit every minute
        _ = Task.Run(async () =>
        {
            while (true)
            {
                await Task.Delay(TimeSpan.FromMinutes(1));
                Interlocked.Exchange(ref _remainingRequests, 100);
                Console.WriteLine("Rate limit reset to 100");
            }
        });
    }

    public async Task<string> GetAsync(string url)
    {
        await _rateLimiter.WaitAsync();

        try
        {
            var request = new OwnRequest(async token =>
            {
                var response = await _client.GetAsync(url, token);

                if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
                {
                    Console.WriteLine("Rate limited by server, retrying...");
                    return false; // Trigger retry
                }

                Interlocked.Decrement(ref _remainingRequests);
                return response.IsSuccessStatusCode;
            }, new()
            {
                Handler = _handler,
                NumberOfAttempts = 5,
                DelayBetweenAttempts = TimeSpan.FromSeconds(10)
            });

            await request.Task;

            if (request.State == RequestState.Completed)
                return "Success";
            else
                throw new Exception("Request failed");
        }
        finally
        {
            _rateLimiter.Release();
        }
    }
}

// Usage:
var client = new RateLimitedApiClient();
var tasks = Enumerable.Range(0, 200)
    .Select(i => client.GetAsync($"https://api.example.com/data/{i}"));

await Task.WhenAll(tasks);

Parallel API Scraping with Container

public class WebScraper
{
    private readonly HttpClient _client = new();

    public async Task<Dictionary<string, string>> ScrapeUrlsAsync(string[] urls)
    {
        var results = new ConcurrentDictionary<string, string>();

        var requests = urls.Select(url => new OwnRequest(async token =>
        {
            var response = await _client.GetAsync(url, token);
            if (!response.IsSuccessStatusCode)
                return false; // Retry

            var html = await response.Content.ReadAsStringAsync(token);
            results[url] = html;

            Console.WriteLine($"Scraped: {url}");
            return true;
        }, new()
        {
            Priority = url.Contains("important") ? RequestPriority.High : RequestPriority.Normal,
            NumberOfAttempts = 3
        })).ToArray();

        // Group in container for unified control
        var container = new RequestContainer<OwnRequest>(requests);

        // Monitor state
        container.StateChanged += (sender, state) =>
        {
            Console.WriteLine($"Container state: {state}");
        };

        await container.Task;

        return results.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
    }
}

$\text{\color{purple}Database Operations}$

Batch Database Insert

public class BatchInserter
{
    private readonly string _connectionString;

    public BatchInserter(string connectionString)
    {
        _connectionString = connectionString;
    }

    public async Task InsertBatchAsync<T>(IEnumerable<T> items, int batchSize = 100)
    {
        var batches = items.Chunk(batchSize);
        var handler = new ParallelRequestHandler { StaticDegreeOfParallelism = 5 };

        foreach (var batch in batches)
        {
            var request = new OwnRequest(async token =>
            {
                using var connection = new SqlConnection(_connectionString);
                await connection.OpenAsync(token);

                using var transaction = connection.BeginTransaction();

                try
                {
                    foreach (var item in batch)
                    {
                        token.ThrowIfCancellationRequested();
                        await InsertItemAsync(connection, transaction, item, token);
                    }

                    await transaction.CommitAsync(token);
                    return true;
                }
                catch
                {
                    await transaction.RollbackAsync(token);
                    return false; // Retry entire batch
                }
            }, new()
            {
                Handler = handler,
                NumberOfAttempts = 3,
                DelayBetweenAttempts = TimeSpan.FromSeconds(5)
            });
        }

        await handler.Task;
    }

    private async Task InsertItemAsync<T>(
        SqlConnection conn,
        SqlTransaction trans,
        T item,
        CancellationToken token)
    {
        // Insert logic
        await Task.Delay(10, token); // Simulate insert
    }
}

Sequential Database Migration

public class DatabaseMigration
{
    public async Task MigrateAsync(string[] migrationScripts)
    {
        var handler = new SequentialRequestHandler(); // One at a time, in order

        foreach (var script in migrationScripts.OrderBy(s => s))
        {
            var request = new OwnRequest(async token =>
            {
                Console.WriteLine($"Executing migration: {script}");

                using var connection = new SqlConnection(_connectionString);
                await connection.OpenAsync(token);

                var sql = await File.ReadAllTextAsync(script, token);
                using var command = new SqlCommand(sql, connection);
                await command.ExecuteNonQueryAsync(token);

                Console.WriteLine($"Completed: {script}");
                return true;
            }, new()
            {
                Handler = handler,
                NumberOfAttempts = 1 // Don't retry migrations
            });
        }

        await handler.Task;
    }
}

$\text{\color{red}Game Development}$

Asset Loading System

public class AssetLoader
{
    private readonly ParallelRequestHandler _handler = new()
    {
        AutoParallelism = () =>
        {
            // Load more assets when GPU is idle
            float gpuUsage = GetGPUUsage();
            return gpuUsage < 0.5f ? 8 : 2;
        },
        MaxParallelism = 8
    };

    public async Task LoadAssetsAsync(AssetManifest manifest)
    {
        // Critical assets (UI, player) load first
        var criticalAssets = manifest.CriticalAssets.Select(asset =>
            new OwnRequest(async token =>
            {
                await LoadAssetAsync(asset, token);
                return true;
            }, new()
            {
                Handler = _handler,
                Priority = RequestPriority.High
            })
        );

        // Background assets (scenery, audio) load later
        var backgroundAssets = manifest.BackgroundAssets.Select(asset =>
            new OwnRequest(async token =>
            {
                await LoadAssetAsync(asset, token);
                return true;
            }, new()
            {
                Handler = _handler,
                Priority = RequestPriority.Low
            })
        );

        // When game is paused/backgrounded
        Application.OnPause += () => _handler.Pause();
        Application.OnResume += () => _handler.Start();

        await _handler.Task;
    }

    private float GetGPUUsage()
    {
        // Platform-specific GPU query
        return 0.3f;
    }
}

Streaming World Chunks

public class WorldStreamer
{
    private readonly ParallelRequestHandler _handler = new()
    {
        StaticDegreeOfParallelism = 4
    };

    public async Task StreamChunksAsync(Vector3 playerPosition, float radius)
    {
        var chunksToLoad = CalculateChunksInRadius(playerPosition, radius);

        var requests = new List<OwnRequest>();

        foreach (var chunkPos in chunksToLoad)
        {
            float distance = Vector3.Distance(playerPosition, chunkPos);

            // Closer chunks have higher priority
            var priority = distance < radius / 3 ? RequestPriority.High :
                          distance < radius * 2 / 3 ? RequestPriority.Normal :
                          RequestPriority.Low;

            var request = new OwnRequest(async token =>
            {
                await Request.Yield(); // Allow interruption for player movement

                var chunkData = await LoadChunkDataAsync(chunkPos, token);
                await GenerateMeshAsync(chunkData, token);

                return true;
            }, new()
            {
                Handler = _handler,
                Priority = priority
            });

            requests.Add(request);
        }

        // Don't wait - chunks load in background
        // await _handler.Task; // Optional
    }
}

$\text{\color{green}Progress Tracking}$

Multi-File Upload with Aggregated Progress

public class FileUploader
{
    public async Task UploadFilesAsync(string[] filePaths, IProgress<float> overallProgress)
    {
        var requests = filePaths.Select(path => new ProgressableFileRequest(path)).ToArray();
        var container = new ProgressableContainer<ProgressableFileRequest>(requests);

        // Subscribe to aggregated progress
        container.Progress.ProgressChanged += (sender, progress) =>
        {
            overallProgress.Report(progress);
        };

        await container.Task;
    }
}

public class ProgressableFileRequest : Request<RequestOptions<VoidStruct, VoidStruct>, VoidStruct, VoidStruct>,
    IProgressableRequest
{
    private readonly string _filePath;
    private readonly Progress<float> _progress = new();

    public Progress<float> Progress => _progress;

    public ProgressableFileRequest(string filePath)
    {
        _filePath = filePath;
        AutoStart();
    }

    protected override async Task<RequestReturn> RunRequestAsync()
    {
        var fileInfo = new FileInfo(_filePath);
        long totalBytes = fileInfo.Length;
        long uploadedBytes = 0;

        using var fileStream = File.OpenRead(_filePath);
        var buffer = new byte[8192];

        while (uploadedBytes < totalBytes)
        {
            await Request.Yield();

            int bytesRead = await fileStream.ReadAsync(buffer, Token);
            if (bytesRead == 0)
                break;

            // Simulate upload
            await Task.Delay(10, Token);

            uploadedBytes += bytesRead;
            ((IProgress<float>)_progress).Report((float)uploadedBytes / totalBytes);
        }

        return new RequestReturn { Successful = true };
    }
}

// Usage:
var uploader = new FileUploader();
await uploader.UploadFilesAsync(
    new[] { "file1.dat", "file2.dat", "file3.dat" },
    new Progress<float>(p => Console.WriteLine($"Upload progress: {p:P}"))
);

Real-Time Dashboard

public class ProcessingDashboard
{
    public async Task ProcessWithDashboardAsync(IEnumerable<string> items)
    {
        var requests = items.Select(item => new ProgressableDataProcessor(item)).ToArray();
        var container = new ProgressableContainer<ProgressableDataProcessor>(requests);

        // Update UI in real-time
        container.StateChanged += (sender, state) =>
        {
            UpdateDashboardState(state);
        };

        container.Progress.ProgressChanged += (sender, progress) =>
        {
            UpdateProgressBar(progress);
        };

        // Display stats every second
        _ = Task.Run(async () =>
        {
            while (container.State != RequestState.Completed)
            {
                await Task.Delay(1000);

                int completed = requests.Count(r => r.State == RequestState.Completed);
                int running = requests.Count(r => r.State == RequestState.Running);
                int failed = requests.Count(r => r.State == RequestState.Failed);

                Console.WriteLine($"Completed: {completed}, Running: {running}, Failed: {failed}");
            }
        });

        await container.Task;
    }
}

$\text{\color{orange}Advanced Patterns}$

Request Pool with Reuse

public class RequestPool<T>
{
    private readonly ConcurrentBag<OwnRequest> _pool = new();
    private readonly Func<T, CancellationToken, Task<bool>> _workFunc;

    public RequestPool(Func<T, CancellationToken, Task<bool>> workFunc)
    {
        _workFunc = workFunc;
    }

    public async Task ExecuteAsync(T item)
    {
        if (!_pool.TryTake(out var request))
        {
            // Create new request
            request = new OwnRequest(async token => await _workFunc(item, token),
                new() { AutoStart = false });
        }

        // Reset and reuse
        request.TrySetIdle();
        request.Start();

        await request.Task;

        // Return to pool
        _pool.Add(request);
    }
}

Conditional Branching Workflow

public async Task ProcessConditionalWorkflowAsync(string dataPath)
{
    var loadRequest = new OwnRequest(async token =>
    {
        var data = await File.ReadAllTextAsync(dataPath, token);
        bool isValid = ValidateData(data);

        if (isValid)
        {
            // Success path
            var processRequest = new OwnRequest(async token =>
            {
                await ProcessValidDataAsync(data, token);
                return true;
            });

            Options.SubsequentRequest = processRequest;
        }
        else
        {
            // Error path
            var errorRequest = new OwnRequest(async token =>
            {
                await LogErrorAsync($"Invalid data in {dataPath}", token);
                return true;
            });

            Options.SubsequentRequest = errorRequest;
        }

        return isValid;
    });

    await loadRequest.Task;
}

Fan-Out/Fan-In Pattern

public async Task<TResult> FanOutFanInAsync<TInput, TResult>(
    TInput[] inputs,
    Func<TInput, CancellationToken, Task<TResult>> processFunc,
    Func<TResult[], TResult> aggregateFunc)
{
    var results = new ConcurrentDictionary<int, TResult>();

    var requests = inputs.Select((input, index) => new OwnRequest(async token =>
    {
        var result = await processFunc(input, token);
        results[index] = result;
        return true;
    })).ToArray();

    var container = new RequestContainer<OwnRequest>(requests);
    await container.Task;

    // Aggregate results in order
    var orderedResults = results.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value).ToArray();
    return aggregateFunc(orderedResults);
}

// Usage:
var sum = await FanOutFanInAsync(
    new[] { 1, 2, 3, 4, 5 },
    async (num, token) =>
    {
        await Task.Delay(100, token);
        return num * 2;
    },
    results => results.Sum()
);

Console.WriteLine($"Sum: {sum}"); // 30

Summary

These examples demonstrate:

  • $\text{\color{lightblue}Basic Patterns}$: Simple requests, configuration, manual control
  • $\text{\color{orange}File Operations}$: Batch downloads, directory scanning, pipelines
  • $\text{\color{blue}HTTP Requests}$: Rate limiting, parallel scraping, API integration
  • $\text{\color{purple}Database Operations}$: Batch inserts, sequential migrations
  • $\text{\color{red}Game Development}$: Asset loading, world streaming, priority management
  • $\text{\color{green}Progress Tracking}$: Multi-file uploads, real-time dashboards
  • $\text{\color{orange}Advanced Patterns}$: Request pooling, conditional workflows, fan-out/fan-in

Adapt these patterns to your specific use cases.

Clone this wiki locally