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

如何从DispatchQueue返回Ping测试结果?Swift异步编程问题

问题解决:从SwiftyPing的DispatchQueue回调中返回测试结果

问题根源

你的代码里waitForDataFromPing调用startPinging后立刻返回self.pingResult,但SwiftyPing的finished回调是异步触发的(还嵌套了DispatchQueue.main.async),此时pingResult还未被赋值,自然拿不到正确结果。另外,全局的latArray和pingResult作为共享状态,在并发请求场景下还会出现数据混乱。

解决方案:用Continuation将回调API转为async/await风格

利用Swift的withCheckedThrowingContinuation,把SwiftyPing的回调逻辑包装成可等待的异步函数,让代码能等待ping测试完成后再返回结果,避免提前返回空值。

修改后的代码示例

替换原有的startPinging和waitForDataFromPing函数

func performPing(hostname: String, n: Int) async throws -> PingTestResult {
    return try await withCheckedThrowingContinuation { continuation in
        do {
            let ping = try SwiftyPing(host: hostname, configuration: PingConfiguration(interval: 1.0, with: 1), queue: DispatchQueue.global())
            ping.targetCount = n
            
            var latArray: [String] = []
            
            ping.observer = { response in
                var latency = String(format: "%.3f ms", response.duration * 1000)
                
                if let error = response.error {
                    if error == .requestTimeout {
                        latency = "Timeout \(latency)"
                    } else {
                        print(error)
                        latency = error.localizedDescription
                    }
                }
                latArray.append(latency)
                print("\nPing Test #\(response.trueSequenceNumber): \(latency)\n")
            }
            
            ping.finished = { result in
                DispatchQueue.main.async {
                    let dateformatter = DateFormatter()
                    dateformatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
                    dateformatter.timeZone = TimeZone.current
                    let date = dateformatter.string(from: Date())
                    
                    guard let roundtrip = result.roundtrip else {
                        continuation.resume(throwing: NSError(domain: "PingError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无往返数据"]))
                        return
                    }
                    
                    guard let packetLoss = result.packetLoss else {
                        continuation.resume(throwing: NSError(domain: "PingError", code: -2, userInfo: [NSLocalizedDescriptionKey: "丢包数据不可用"]))
                        return
                    }
                    
                    let rtMin = String(format: "%.3f ms", roundtrip.minimum * 1000)
                    let rtMax = String(format: "%.3f ms", roundtrip.maximum * 1000)
                    let rtAvg = String(format: "%.3f ms", roundtrip.average * 1000)
                    let rtStdDev = String(format: "%.3f ms", roundtrip.standardDeviation * 1000)
                    
                    // 打印统计信息
                    var message = "\n--- \(hostname) ping statistics ---\n"
                    message += "\n测试时间: \(date)\n"
                    message += "\n测试次数: \(n)\n"
                    message += "\n发送数据包: \(result.packetsTransmitted)\n"
                    message += "\n接收数据包: \(result.packetsReceived)\n"
                    message += String(format: "\n丢包率: %.1f%% \n", packetLoss * 100)
                    message += "\n往返最小延迟: \(rtMin)"
                    message += "\n往返最大延迟: \(rtMax)"
                    message += "\n往返平均延迟: \(rtAvg)"
                    message += "\n往返延迟标准差: \(rtStdDev)"
                    
                    print(message)
                    
                    // 构造结果并返回
                    let pingResult = PingTestResult(
                        latency: latArray,
                        Date: date,
                        packetsTrasmitted: result.packetsTransmitted,
                        packetsReceived: result.packetsReceived,
                        packetLoss: packetLoss * 100,
                        rtMin: rtMin,
                        rtMax: rtMax,
                        rtAvg: rtAvg,
                        rtStdDev: rtStdDev
                    )
                    
                    continuation.resume(returning: pingResult)
                }
            }
            
            try ping.startPinging()
        } catch {
            continuation.resume(throwing: error)
        }
    }
}

修改路由接口调用

routes.post("ping") { req async throws -> PingTestResult in
    let pingConfig = try req.content.decode(PingConfig.self)
    print("hostname: \(pingConfig.host!), to be ping: \(pingConfig.n!)\n")
    return try await performPing(hostname: pingConfig.host!, n: pingConfig.n!)
}

关键说明

  • Continuation的作用:withCheckedThrowingContinuation会暂停当前异步任务,直到调用continuation.resume(),确保等待SwiftyPing的finished回调执行完成后再返回结果。
  • 移除全局状态:用局部变量latArray存储延迟数据,避免多个请求之间的状态污染。
  • 错误处理:在回调里检查必要的结果字段,缺失时抛出错误,符合async throws的函数签名。
  • 修正原代码错误:原代码中打印统计信息时重复写了“Round-trip Min”,已修正为对应字段。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 05:15:36