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

点击UIButton时Swift的Task与异步API执行顺序及返回nil问题排查

问题根源

你的代码存在异步操作的竞态问题:

  • performRequest方法中,URLSession.dataTask(with:completionHandler:)是异步执行的网络请求,调用task.resume()后,方法会立刻往下执行并返回初始值nil,不会等待网络请求完成。
  • 网络请求完成后,才会进入completion闭包解析数据并赋值给weatherResult,但此时fetchWeather已经把nil返回给调用者了,所以会先打印weatherData: nil,之后才打印解析完成的decodedData。

另外,你在ViewController里用了Task和await,但fetchWeather并不是异步函数(没有async关键字),所以await在这里完全不起作用,无法等待网络请求完成。

修复方案

把WeatherManager的方法改成异步函数,利用Swift的async/await替代传统的completion handler,确保调用者能等待网络请求完成后拿到结果:

修改后的WeatherManager代码

struct WeatherManager{
    let weatherURL = "https://api.openweathermap.org/data/2.5/weather?appid=someAPIKeyHere"
    
    // 改成异步函数,返回WeatherModel?
    func fetchWeather(cityName: String) async -> WeatherModel? {
        let urlString = "\(weatherURL)&q=\(cityName)"
        return await performRequest(urlString: urlString)
    }
    
    // 异步函数,用async/await处理网络请求
    func performRequest(urlString: String) async -> WeatherModel? {
        guard let url = URL(string: urlString) else {
            return nil
        }
        
        do {
            // 用URLSession的异步方法替代completion handler
            let (data, _) = try await URLSession.shared.data(from: url)
            return parseJSON(weatherData: data)
        } catch {
            print("网络请求错误: \(error.localizedDescription)")
            return nil
        }
    }
    
    func parseJSON(weatherData: Data) -> WeatherModel?{
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(WeatherResponse.self, from: weatherData)
            print("this is in decodedData: \(decodedData)")
            let temp = decodedData.main.temp
            let name = decodedData.name
            let weather = WeatherModel(conditionId:300, cityName: name, temperature: temp)
            return weather
        } catch {
            print("解析错误: \(error.localizedDescription)")
            return nil
        }
    }
}

修改后的ViewController代码

@IBAction func callAPIButton(_ sender: UIButton) {
    Task {
        // 现在await会等待fetchWeather异步执行完成
        let weatherData = await weatherManager.fetchWeather(cityName: "Seattle")
        print("This is in weatherData: \(weatherData ?? WeatherModel(conditionId: 0, cityName: "", temperature: 0.0))")
    }
}
修复后效果

执行后会先等待网络请求和解析完成,再打印weatherData的内容,输出顺序和你期望的一致:

this is in decodedData: WeatherResponse(name: "Seattle", weather: [Awesome_Weather_App.WeatherAPI(description: "overcast clouds", icon: "04d")], main: Awesome_Weather_App.MainAPI(temp: 287.81, pressure: 1018.0, humidity: 44.0, temp_min: 284.91, temp_max: 290.42, feels_like: 286.48), sys: Awesome_Weather_App.SysAPI(sunrise: 1.6712886e+09, sunset: 1.6713243e+09))
This is in weatherData: WeatherModel(conditionId: 300, cityName: "Seattle", temperature: 287.81)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 12:01:07