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

SwiftUI并发后台线程失效:实时计算结果无法更新

问题分析与修复方案

问题根源

  1. 计算触发逻辑错误:当前代码中ResultView的.task修饰符仅在视图首次初始化时执行一次异步计算方法,当input1或input2更新时,不会重新触发计算,导致结果无法实时同步。
  2. 休眠时间参数错误:Task.sleep(nanoseconds: 1000)的单位是纳秒,1000纳秒仅为1微秒,无法模拟后台计算的耗时场景,应改为1_000_000_000(1秒)。
  3. 缺少防除零判断:除法计算未做非零校验,存在运行时崩溃风险。

修复后的代码

DataModel 修改

import Combine
import SwiftUI

class DataModel: ObservableObject {
    @Published var input1: Double? = nil
    @Published var input2: Double? = nil
    
    @Published var multiplication: Double = 0
    @Published var input1by2: Double = 0
    @Published var input2by1: Double = 0
    
    // 管理异步任务与订阅
    private var cancellables = Set<AnyCancellable>()
    private var multiplyTask: Task<Void, Never>?
    private var division1Task: Task<Void, Never>?
    private var division2Task: Task<Void, Never>?
    
    init() {
        // 监听输入变化,自动触发计算
        Publishers.CombineLatest($input1, $input2)
            .sink { [weak self] _, _ in
                self?.triggerCalculations()
            }
            .store(in: &cancellables)
    }
    
    private func triggerCalculations() {
        // 取消旧任务,避免重复计算
        multiplyTask?.cancel()
        division1Task?.cancel()
        division2Task?.cancel()
        
        // 启动新计算任务
        multiplyTask = Task { await multiply() }
        division1Task = Task { await division1() }
        division2Task = Task { await division2() }
    }
    
    func multiply() async {
        guard
            let input1 = input1,
            let input2 = input2,
            !Task.isCancelled
        else { return }
        
        // 模拟1秒后台计算耗时
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        guard !Task.isCancelled else { return }
        
        let value = input1 * input2
        await MainActor.run {
            self.multiplication = value
        }
    }
    
    func division1() async {
        guard
            let input1 = input1,
            let input2 = input2,
            input2 != 0,
            !Task.isCancelled
        else { 
            await MainActor.run {
                self.input1by2 = 0
            }
            return 
        }
        
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        guard !Task.isCancelled else { return }
        
        let value = input1 / input2
        await MainActor.run {
            self.input1by2 = value
        }
    }
    
    func division2() async {
        guard
            let input1 = input1,
            let input2 = input2,
            input1 != 0,
            !Task.isCancelled
        else { 
            await MainActor.run {
                self.input2by1 = 0
            }
            return 
        }
        
        try? await Task.sleep(nanoseconds: 1_000_000_000)
        guard !Task.isCancelled else { return }
        
        let value = input2 / input1
        await MainActor.run {
            self.input2by1 = value
        }
    }
}

ResultView 修改

struct ResultView: View {
    @EnvironmentObject var tap: DataModel
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack(spacing: 0) {
                Text("Multiplication: ")
                Text("\(tap.multiplication, specifier: \"%.2f\")")
            }
            
            HStack(spacing: 0) {
                Text("Input1/Input2: ")
                Text("\(tap.input1by2, specifier: \"%.2f\")")
            }
            
            HStack(spacing: 0) {
                Text("Input2/Input1: ")
                Text("\(tap.input2by1, specifier: \"%.2f\")")
            }
        }
        .font(.title3)
        .foregroundStyle(Color.primary)
    }
}

关键改进点

  • 自动触发计算:通过CombineLatest监听输入变化,实时启动新计算任务并取消旧任务,确保结果始终对应最新输入。
  • 任务安全校验:在异步计算中加入Task.isCancelled判断,避免已过时的计算结果覆盖最新值。
  • 防崩溃处理:除法计算增加非零校验,避免运行时崩溃。
  • 真实场景模拟:修正休眠时间为1秒,贴近实际后台计算的耗时情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:15:23