Swift并发编程:async/await实战——一个被裁全栈的深夜顿悟

木木在敲代码
2025-12-23 14:29
阅读 1527

上周五晚上11点47分,我坐在杭州出租屋的书桌前,窗外下着小雨。MacBook Pro风扇嗡嗡作响,屏幕上是第17次调试失败的Swift代码。手机震动了一下,是我老婆发来的消息:“今天项目做完了吗?别熬太晚,明天还要坐高铁过来。”
我盯着那行 Task { await fetchData() },手指悬在键盘上,突然鼻子一酸。

去年十月,我还在北京一家中型互联网公司做全栈开发,月薪15k,朝九晚六(虽然经常加班到九点),和老婆住在回龙观合租的次卧里,月租3500。那时日子虽然紧巴巴,但至少安稳。直到那天下午三点,HR把我叫进会议室,说“公司战略调整”,递给我一张N+1的赔偿协议。走出公司大楼时,北京的秋风刮得人脸生疼,我站在路边点了根烟,手都在抖——不是因为冷,是因为脑子里一片空白:房贷怎么办?老婆刚怀孕三个月,产检的钱从哪来?

那一周,我投了87份简历,面试了6家,不是嫌我“年纪偏大”(其实才29),就是开价比之前还低。最后咬牙决定:不找了,自己接外包。靠着之前积累的人脉,勉强接了两个小项目,一个是用React+Node.js做的后台管理系统,另一个是帮朋友公司维护一个iOS App。收入不稳定,但好歹能糊口。老婆看我状态不好,主动提出回老家待产,她说:“你先稳住自己,我这边有爸妈照顾。” 我们就这样开始了异地生活,每周五晚上她坐高铁来杭州,周日晚上回去。车票钱一个月近2000块,但我俩都觉得值——至少见面的时候,能互相充个电。


从JavaScript回调地狱到Swift并发天堂

说回技术。我最早接触异步编程,是在写JavaScript的时候。记得2016年刚入行,用jQuery写AJAX请求,一层套一层的回调函数,代码缩进到屏幕右边快看不见了,同事戏称“回调金字塔”。后来有了Promise,再后来async/await横空出世,世界清净了。写JS的时候,我可以这样优雅地获取用户数据:

async function getUserProfile(userId) {
  const user = await fetchUser(userId);
  const posts = await fetchPosts(userId);
  return { user, posts };
}

简洁、直观、线性思维——这才是人该写的代码!

但当我接手那个iOS外包项目时,发现它还是用传统的Completion Handler写法:

func loadUserProfile(userId: String, completion: @escaping (UserProfile?, Error?) -> Void) {
    fetchUser(userId: userId) { user, error in
        if let error = error {
            completion(nil, error)
            return
        }
        fetchPosts(userId: userId) { posts, error in
            if let error = error {
                completion(nil, error)
                return
            }
            let profile = UserProfile(user: user!, posts: posts!)
            completion(profile, nil)
        }
    }
}

天啊,这不就是Swift版的回调地狱吗?而且更糟——嵌套多了之后,连错误处理都变得极其繁琐。每次调用都要写一大坨if-let判断,稍不注意就内存泄漏(忘了weak self)。我一边改bug一边骂:“这代码是谁写的?哦,是我上周写的……”

当时真的很焦虑。客户催得急,老婆下周就要来做产检,我得赶在她来之前把这破App的崩溃问题修好。可每次测试,总在并发请求时出现奇怪的数据错乱。我知道问题出在异步处理上,但老式的GCD(Grand Central Dispatch)写法太容易出错了——dispatch group、semaphore、barrier……这些概念我懂,但在实际项目里用起来,就像在雷区里跳格子,一步错,全盘崩。


转机:深夜重读Swift文档

事情的转机发生在一个周二凌晨。那天又加班到很晚,老婆发微信说:“宝宝今天踢我了,超有力!” 我笑着回了个表情包,然后突然意识到:我不能再用“临时工心态”对待这个项目了。就算只是外包,也得对得起自己的名字。于是,我关掉所有无关的浏览器标签,打开Apple官方文档,重新学习Swift 5.5引入的async/await

说实话,一开始我觉得“这不就是JS那套嘛”,但深入后才发现,Swift的并发模型要严谨得多。它不只是语法糖,而是整个语言层面的重构。最打动我的是结构化并发(Structured Concurrency) 的概念——任务(Task)有明确的生命周期,不会像GCD那样容易产生“孤儿任务”。

我决定重构整个网络层。目标很明确:

  1. 消灭所有completion handler
  2. 错误处理集中化
  3. 支持取消请求(比如用户退出页面时)

第一步,我把所有网络请求方法改成async函数:

// 旧写法(Completion Handler)
func fetchUser(id: String, completion: @escaping (User?, Error?) -> Void)

// 新写法(async/await)
func fetchUser(id: String) async throws -> User

第二步,用TaskTaskGroup处理并行请求。比如加载用户主页需要同时获取用户信息、帖子列表、好友动态,以前得用dispatch group手动管理,现在一行代码搞定:

func loadProfile(userId: String) async throws -> Profile {
    let (user, posts, friends) = try await (
        fetchUser(id: userId),
        fetchPosts(userId: userId),
        fetchFriends(userId: userId)
    )
    return Profile(user: user, posts: posts, friends: friends)
}

看到没?和JavaScript的写法几乎一样!但背后是Swift强大的类型安全和内存管理在支撑。再也不用担心弱引用捕获的问题了。

第三步,处理取消。Swift的Task天然支持取消,配合withTaskCancellationHandler,可以优雅地清理资源:

let task = Task {
    do {
        let data = try await fetchData()
        updateUI(data)
    } catch {
        if Task.isCancelled {
            print("任务已取消")
        } else {
            handleError(error)
        }
    }
}

// 当用户离开页面时
task.cancel()

整个重构花了我三天时间,但效果立竿见影。崩溃率降为0,并发请求不再互相干扰,代码行数减少了30%,最关键的是——我终于能睡个好觉了。


和老婆的周末:代码之外的生活

上周末,老婆来了杭州。周六早上,她躺在沙发上刷那个我重构过的App,突然说:“诶,怎么这次打开速度快了好多?而且滑动特别流畅。”
我正煎蛋,回头笑着说:“因为我把那些‘祖传代码’给干掉了。”
她不懂技术,但知道我在努力,就拍拍身边的位置:“来,歇会儿,陪我看会儿剧。”

那一刻,我突然觉得,技术再酷,也不过是为了让生活少一点焦虑,多一点陪伴。被裁员那会儿,我总觉得自己是个失败者,但现在想通了:人生没有白走的路。正是因为做过前端(JavaScript),我才更能体会到Swift并发模型的优越性;正是因为经历过失业的恐慌,我才更珍惜每一个能靠技术吃饭的日子。


给同行的几点建议

如果你也在用Swift做项目,尤其是维护老代码,我真心建议你尽快拥抱async/await。别被“iOS版本兼容”吓退——现在iOS 15+的覆盖率已经超过85%,新项目完全可以直接用。如果是老项目,可以逐步迁移:先从工具类方法开始,比如网络请求、数据库操作,慢慢替换掉completion handler。

另外,别小看“结构化并发”这个概念。它不只是语法改进,而是一种思维范式的升级。以前我们写异步代码,总想着“怎么不让线程打架”,现在Swift告诉我们:“别管线程,只管任务”。这种抽象层级的提升,能极大减少心智负担。

最后,说点掏心窝子的话:技术人容易陷入“工具崇拜”,觉得学了新框架、新语言就能翻身。但现实是,技术只是手段,生活才是目的。我见过太多同行为了追新技术熬夜秃头,却忽略了身边人的感受。我现在接外包,虽然收入不如大厂稳定(上个月赚了18k,比之前少,但时间自由),但至少每周能见到老婆,能陪她做产检,能在她半夜孕吐时递上一杯温水。


写在最后:在不确定的世界里,做确定的事

今天是2024年6月15日,老婆的预产期还有两个月。我刚刚提交了项目的最后一个PR,客户回复:“体验流畅多了,辛苦了!” 窗外雨停了,杭州的夏天闷热潮湿,但我的心里很平静。

从JavaScript的回调地狱,到Swift的async/await天堂,技术一直在进化。但不变的是:我们写代码,终究是为了服务人,而不是被代码奴役。被裁员不可怕,可怕的是在焦虑中迷失自己。现在的我,依然会为房租发愁(下季度涨到3800了),依然会担心接不到新项目,但我不再恐惧。因为我相信,只要手还能敲代码,心还能感受爱,日子就总有奔头。

如果你也在经历职业低谷,不妨试试:

  • 把一个老项目用新技术重构一遍(哪怕只是练习)
  • 每周留出固定时间陪伴重要的人
  • 接受收入波动,但别放弃对技术的热情

毕竟,人生不是一场冲刺,而是一次长跑。偶尔跌倒,拍拍灰,继续往前走就好。

(完)


作者:一个被裁后靠接外包养家的全栈开发,现居杭州,周末兼职“准爸爸”。如果你也有类似经历,欢迎邮件交流:dev.with.heart@example.com(假邮箱,别真发)

评论 0

最热最新
暂无评论
木木在敲代码Lv.1
0
影响力
0
文章
0
粉丝