Swift并发编程:async/await实战——一个成都前测试转开发的血泪心得

代码与远方
2025-12-15 13:53
阅读 752

大家好,我是小杨,坐标成都,一个从测试工程师“叛逃”到iOS开发三年的打工人。现在在一家本地SaaS公司写Swift,月薪22k(税前),房租3500,老婆刚怀孕三个月——对,就是那种既不敢裸辞又想偷偷摸鱼学新技术的状态。

今天不聊职业规划,也不吐槽成都码农工资低得像被泡在火锅底料里腌入味了,咱们聊聊Swift并发编程里的async/await。这事得从去年十月说起。


一、起因:那个让我半夜惊醒的爬虫任务

去年十月初,公司接了个新需求:要做一个内部数据监控平台,每天凌晨自动抓取竞品App的版本更新日志、评分变化、关键词排名……说白了,就是个轻量级爬虫系统,跑在macOS后台。

我主动揽下了这活——毕竟之前做测试时就写过Python爬虫,以为不过是换件衣服的事。结果刚开工三天,就被卡住了。

我们的目标App信息分散在多个页面,有些接口还带token验证,请求之间还有依赖关系。比如:先登录 → 获取用户ID → 拿ID查详情 → 再拿详情里的字段去另一个接口拉数据……

用传统的Completion Handler?代码直接嵌套五层,像极了我在春熙路排队买奶茶时的心情——又急又乱。更糟的是,任务一多,主线程直接卡死,Mac风扇狂转,吓得我赶紧Command + .终止进程。

那天晚上11点,我瘫在沙发上刷技术群,看到有人发了一张书页截图:

“使用 async/await 可以让异步代码看起来像同步一样线性执行。”

配图是《Modern Concurrency in Swift》——一本讲Swift并发的英文书。我当时心想:这不就是救星吗?

但问题是,我英语阅读速度堪比蜗牛爬青城山,而且这本书在国内连纸质版都买不到,电子版要80刀(约570块人民币)。我盯着屏幕,内心疯狂挣扎:“值不值?会不会又是看了三页就吃灰?”

最后咬咬牙,用老婆的支付宝花呗买了——理由是“投资自己”。她翻了个白眼:“上次你说学Rust也是这么说的,结果呢?”


二、入门:从“看不懂”到“好像能跑”

拿到书之后,我花了整整一周,每天下班后啃两小时。说实话,前两章直接劝退:TaskActorstructured concurrency……术语多得像火锅里的毛肚,数都数不清。

但我不甘心。想起以前做自动化测试时,也是一步步从Appium小白熬过来的。于是决定边学边写——把那个爬虫项目当成练兵场。

第一个突破点是:把一个简单的网络请求改成async函数

以前的写法:

func fetchVersion(completion: @escaping (String?) -> Void) {
    URLSession.shared.dataTask(with: url) { data, _, _ in
        let version = String(data: data!, encoding: .utf8)
        completion(version)
    }.resume()
}

改成async/await后:

func fetchVersion() async throws -> String {
    let (data, _) = try await URLSession.shared.data(from: url)
    return String(decoding: data, as: UTF8.self)
}

天啊!没有回调地狱了!调用的时候直接:

let version = try await fetchSJVersion()
print("当前版本:\(version)")

那一刻,我差点在出租屋里喊出声:“这TM才是人写的代码!”

但很快,现实给了我一巴掌。


三、实战踩坑:并发不是万能胶水

既然单个请求能搞定,那我把整个爬虫流程全async化吧!信心满满地写了这么一段:

let loginResult = try await login()
let userInfo = try await getUserInfo(userId: loginResult.id)
let appDetails = try await getAppDetails(appId: userInfo.appId)
let keywords = try await getKeywords(rankId: appDetails.rankId)

逻辑清晰,顺序执行,完美!

可一跑起来,发现整个流程耗时40秒。而竞品数据每5分钟更新一次,这意味着我的爬虫根本追不上节奏。

问题出在哪?我把本该并发的操作写成了串行!

其实getAppDetailsgetKeywords完全可以并行请求——它们不依赖彼此。但我傻乎乎地用await一个接一个等,等于主动放弃了并发优势。

这时候,书里提到的async letTaskGroup派上用场了。

我改成这样:

async let detailsTask = getAppDetails(appId: userInfo.appId)
async let keywordsTask = getKeywords(rankId: appDetails.rankId)

let (details, keywords) = try await (detailsTask, keywordsTask)

或者用withTaskGroup批量处理多个独立请求:

var results: [String] = []
await withTaskGroup(of: String.self) { group in
    urls.forEach { url in
        group.addTask {
            return try await fetchData(from: url)
        }
    }
    
    for try await result in group {
        results.append(result)
    }
}

改完一测,总耗时从40秒降到12秒!那一刻,我感觉自己的CPU都超频了。


四、资源整理:别再只看官方文档了

很多人学Swift并发,第一反应是去翻Apple官方文档。但说实话,官方文档写得……嗯,很“苹果”——概念精准但缺乏场景。

除了那本《Modern Concurrency in Swift》(强烈推荐,虽然贵但值得),我还挖到几个宝藏中文资源

  1. 《Swift并发编程入门》(GitHub开源项目):作者是位上海的iOS老哥,用大量生活化比喻讲解Task和Actor,比如把Actor比作“独占厕所”,只有一个人能进。
  2. B站UP主“喵神” 的async/await系列视频:虽然更新慢,但每集都直击痛点,尤其是讲“结构化并发”那期,让我豁然开朗。
  3. Ray Wenderlich官网的Swift并发教程:免费章节就够用,例子全是真实App场景,比如下载图片、处理用户输入等。

另外,我建了个Notion页面,专门收集Swift并发相关的问题和解决方案。比如:

  • 如何在非async函数里调用async函数?(答案:用Task { }
  • 如何取消一个正在进行的Task?(用Task.isCancelled检查)
  • Actor怎么避免死锁?(别在actor方法里await另一个actor的方法)

这些碎片知识,光看书是记不住的,必须在真实项目里摔几次跤才能长记性。


五、反思:为什么我当初那么抗拒学并发?

说实话,刚转开发那会儿,我对“并发”“多线程”这类词有PTSD。因为在测试岗时,每次遇到App卡顿、闪退,开发都会甩一句:“是不是你并发测试搞的?”——搞得我像罪魁祸首。

后来才明白,并发不是洪水猛兽,而是工具。就像菜刀,用得好能切出宫保鸡丁,用不好可能剁到手指。关键在于理解它的边界和规则。

而Swift的async/await设计哲学,恰恰是降低并发的使用门槛。它用编译器帮你检查数据竞争,用结构化并发防止任务泄漏——这对我这种半路出家、基础不牢的人来说,简直是救命稻草。


六、给同行的建议:别怕“晚”,就怕“停”

我知道很多转行的朋友(尤其是从测试、产品转开发的)会有种“我基础差,学不动新东西”的焦虑。我自己也经历过:去年面试时,被问到“Actor和Class的区别”,当场懵圈,回来查资料才发现自己连基本概念都没搞清。

但我想说:技术栈可以慢慢补,思维惯性才是最大的敌人

async/await不是银弹,但它代表了一种趋势:异步编程正在变得越来越“同步化”。未来不管是Swift、Kotlin还是JavaScript,都会朝着这个方向演进。早点掌握,就少走弯路。

而且,实践是最好的老师。别等到“准备好了”再动手——我就是在那个破爬虫项目里,硬着头皮改了十几版代码,才真正理解什么是“结构化并发”。


七、结尾:在成都,做个快乐的“慢”程序员

上周五晚上,我终于把爬虫系统上线了。老板拍我肩膀说:“效率提升明显,下季度给你加薪。” 我心里盘算:要是能涨到25k,就能换个离地铁近点的房子,老婆产检也方便些。

回家路上,路过小区门口的烧烤摊,点了份烤苕皮,配冰啤酒。看着手机里Xcode控制台输出的“所有任务完成,耗时9.3秒”,突然觉得——在成都这座“慢城市”里,做一个不断学习的程序员,其实挺幸福的。

工资不高,但生活成本低;技术更新快,但社区资源多。只要不停下脚步,哪怕是从测试转来的“野路子”,也能写出优雅的async/await代码。

共勉。


P.S. 如果你也正在学Swift并发,欢迎私信交流(虽然我可能回得慢,毕竟要陪老婆产检 😅)。顺便,那本《Modern Concurrency in Swift》,我可以分享PDF笔记——但请支持正版,毕竟作者也得吃饭,对吧?

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝