Swift 中调用 async 函数后循环为何没有等待回调?
结论
async 只表示函数可以被 await 调用,不会自动等待函数内部启动的 completion 回调。原来的 updateItemQuantity 调用 API 后可能立即返回,循环便会继续执行,最后提前清空数组。
例如,原函数的 API 调用形式是:
func updateItemQuantity(itemCode: String, quantity: Int, onComplete: @escaping (Int) -> ()) async {
Service.sharedInstance.updateItemQuantityApi(
scannedCode: itemCode,
quantity: quantity,
expandedSearch: false,
completion: { cartDTO in
// 处理结果
onComplete(resultCart.cartItems.count)
}
)
}
函数没有用 await 等待 completion。因此,外层的 await updateItemQuantity(...) 只会等到这个函数返回,不会等 API 回调执行完毕。
用 continuation 等待 API 回调
可以把回调结果作为异步函数的返回值,让 await 一直等到 API 完成。下面根据你更新后的代码结构简化了示例:
func updateItemQuantity(itemCode: String, quantity: Int) async -> Int {
await withCheckedContinuation { continuation in
Service.sharedInstance.updateItemQuantityApi(
scannedCode: itemCode,
quantity: quantity,
expandedSearch: false,
completion: { cartDTO in
let resultCart = Cart(from: cartDTO)
// 在这里更新 cart、提示状态等数据
continuation.resume(returning: resultCart.cartItems.count)
},
failure: { alertType, errorText in
// 在这里处理错误状态
continuation.resume(returning: 0)
}
)
}
}
然后在循环中等待函数返回结果:
for offlineItem in offlineItems {
let count = await updateItemQuantity(
itemCode: offlineItem.scannedCode,
quantity: offlineItem.quantity
)
if count > 1 {
showItemSelect = true
// 等待用户选择后再处理下一项
}
}
原来更新 cart、itemsToSelect 和提示状态的逻辑,可以放进对应的回调中。withCheckedContinuation 必须在每条可能执行的路径上恰好调用一次 resume。你更新后的代码只展示了在线 API 路径,离线分支也需要返回结果,否则调用方会一直挂起。如果需要区分 API 错误和数量为 0,可以让异步函数返回 Result<Int, Error>。
用户选择需要单独等待
API 完成和用户完成选择需要分别处理。数量大于 1 时,设置 showItemSelect = true 不会让异步函数自动暂停;循环也不应在 isPaused 为真时反复空转。可以让处理任务在这里等待用户选择完成,例如使用由选择处理器恢复的 continuation,或通过 AsyncStream 传递选择结果。用户完成选择且上传成功后,再由同一个处理流程继续下一项并推进索引,避免多个位置同时修改 currentIndex。
还需检查的边界
你提供的 continuation 版本在失败回调中以 0 恢复,调用方会把 API 失败当作“未找到商品”。应根据业务需要区分失败和数量为 0。另外,访问 resultCart.cartItems[0] 前应确认数组非空,否则空结果会导致越界。
消息中提到有 Xcode 截图,但没有附上截图内容,因此无法判断截图中的具体编译错误。如果采用 continuation 后仍有编译问题,需要结合完整错误文本和 API 实际的 completion、failure 签名确认。
备注:内容仅供参考。
