PAYATHON 2026

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 签名确认。

备注:内容仅供参考。