-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAsyncHelpers.swift
More file actions
102 lines (89 loc) · 3.52 KB
/
Copy pathAsyncHelpers.swift
File metadata and controls
102 lines (89 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//
// AsyncHelpers.swift
// Cardano
//
// Created by hellc.
// Copyright © 2020-2026 KXP. All rights reserved.
// Licensed under the MIT License.
//
import Foundation
/// Helper utilities for async/await operations in the Cardano API.
/// Provides convenient wrappers and patterns for parallel processing.
///
/// ### Example
/// ```swift
/// let results = try await AsyncHelpers.processInParallel(items: myItems) { item in
/// try await process(item)
/// }
/// ```
public class AsyncHelpers {
/// Executes a closure on a background thread with high priority.
/// Useful for offloading synchronous CPU-bound tasks.
/// - Parameter block: The synchronous block to execute.
/// - Returns: The result of the block execution.
public static func runOnBackgroundThread<T>(_ block: @escaping () throws -> T) async throws -> T {
return try await Task.detached(priority: .userInitiated) {
try block()
}.value
}
/// Processes multiple items in parallel with a maximum concurrency limit.
/// Prevents resource exhaustion when processing large batches.
/// - Parameters:
/// - items: The items to process.
/// - maxConcurrency: The maximum number of concurrent operations (default: 4).
/// - block: The async operation to perform on each item.
/// - Returns: An array of results in the same order as input items.
public static func processInParallel<T, U>(
items: [T],
maxConcurrency: Int = 4,
block: @escaping (T) async throws -> U
) async throws -> [U] {
let results = try await withThrowingTaskGroup(of: (Int, U).self, returning: [U].self) { group in
var activeCount = 0
var itemIterator = items.enumerated().makeIterator()
while activeCount < maxConcurrency, let (idx, item) = itemIterator.next() {
group.addTask {
return try await (idx, block(item))
}
activeCount += 1
}
var resultArray = [(Int, U)]()
for try await (idx, result) in group {
resultArray.append((idx, result))
if let (nextIdx, nextItem) = itemIterator.next() {
group.addTask {
return try await (nextIdx, block(nextItem))
}
}
}
return resultArray.sorted { $0.0 < $1.0 }.map { $0.1 }
}
return results
}
/// Retries an async operation with exponential backoff on failure.
/// - Parameters:
/// - maxAttempts: The maximum number of retry attempts.
/// - initialDelay: The initial delay in milliseconds between retries.
/// - block: The async operation to retry.
/// - Returns: The result of successful execution.
public static func retryWithBackoff<T>(
maxAttempts: Int = 3,
initialDelay: UInt64 = 100,
block: @escaping () async throws -> T
) async throws -> T {
var lastError: Error?
var delay = initialDelay
for attempt in 0..<maxAttempts {
do {
return try await block()
} catch {
lastError = error
if attempt < maxAttempts - 1 {
try await Task.sleep(nanoseconds: delay * 1_000_000)
delay *= 2 // Exponential backoff
}
}
}
throw lastError ?? CardanoError.invalidPath
}
}