You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中实现出站网络请求限流及视图任务管理方案

解决SwiftUI中带速率限制的网络请求队列问题

核心需求拆解

  • 遵守API的1 RPS速率限制,避免触发限流
  • 单个请求失败不中断其他请求的执行
  • 视图导航时自动取消该视图发起的未完成请求,释放队列资源

解决方案实现

1. 全局速率限制器(RateLimiter)

用Actor实现线程安全的请求队列,支持任务取消和速率控制:

import Foundation
import AsyncAlgorithms

actor RateLimiter {
    private let queue = AsyncChannel<(taskID: UUID, operation: @Sendable () async throws -> Void)>()
    private let interval: Duration = .seconds(1)
    private var pendingTasks: Set<UUID> = []
    
    init() {
        Task {
            await processQueue()
        }
    }
    
    private func processQueue() async {
        for await (taskID, operation) in queue {
            guard pendingTasks.contains(taskID) else { continue }
            
            let start = ContinuousClock.now
            do {
                try await operation()
            } catch is CancellationError {
                print("任务 \(taskID) 已取消")
            } catch {
                print("任务 \(taskID) 执行失败: \(error)")
            }
            pendingTasks.remove(taskID)
            
            let elapsed = ContinuousClock.now - start
            let remaining = interval - elapsed
            if remaining > .zero {
                try? await Task.sleep(for: remaining)
            }
        }
    }
    
    /// 提交请求到队列,返回任务ID用于取消
    func submit(operation: @Sendable @escaping () async throws -> Void) -> UUID {
        let taskID = UUID()
        pendingTasks.insert(taskID)
        Task {
            await queue.send((taskID, operation))
        }
        return taskID
    }
    
    /// 取消指定ID的未执行任务
    func cancel(taskID: UUID) {
        pendingTasks.remove(taskID)
    }
}

2. 全局共享实例

在App级别注入全局RateLimiter:

@main
struct MyApp: App {
    @State private let rateLimiter = RateLimiter()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(rateLimiter)
        }
    }
}

3. 视图中使用

在视图中发起请求,绑定视图生命周期,自动取消未完成任务:

struct SomeView: View {
    let id: String
    @Environment(RateLimiter.self) private var rateLimiter
    @State private var taskIDs: [UUID] = []
    
    private func loadData() async {
        // 提交评论请求
        let commentTaskID = rateLimiter.submit {
            try await api.loadUserComments(id)
            // 成功后更新SwiftData
        }
        taskIDs.append(commentTaskID)
        
        // 提交用户数据请求
        let userDataTaskID = rateLimiter.submit {
            try await api.loadUserData(id)
            // 成功后更新SwiftData
        }
        taskIDs.append(userDataTaskID)
        
        // 提交用户帖子请求
        let postsTaskID = rateLimiter.submit {
            try await api.loadUserPosts(id)
            // 成功后更新SwiftData
        }
        taskIDs.append(postsTaskID)
    }
    
    var body: some View {
        Text("加载中...")
            .task(id: id) {
                await loadData()
                // 任务结束后清空本地记录(可选)
                taskIDs.removeAll()
            }
            .onDisappear {
                // 视图消失时取消所有未完成的任务
                for taskID in taskIDs {
                    rateLimiter.cancel(taskID: taskID)
                }
            }
    }
}

方案优势

  • 速率控制:RateLimiter保证请求严格按照1 RPS执行,自动补偿请求执行时间不足1秒的间隔
  • 容错性:单个请求失败不会影响其他请求,错误被单独捕获处理
  • 生命周期绑定:视图消失时自动取消对应未执行任务,避免无效请求占用队列资源
  • 线程安全:Actor确保队列操作和任务管理的线程安全

内容的提问来源于stack exchange,提问作者sbdchd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:04:55