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

如何处理sendMessage异步调用?WatchKit是否自动实现请求排队?

解决WCSession.sendMessage的异步排队问题

首先明确说:WatchKit不会自动实现消息排队机制,官方文档里确实没有相关说明,所以你得自己处理多个异步触发的sendMessage请求。

不用写复杂的自定义队列类,有两种简便的实现方式,都是基于系统原生的队列机制:

方法一:串行DispatchQueue+请求数组(轻量推荐)

这种方式用一个串行队列来管理请求的入队和出队,搭配一个数组存储待处理的请求,再用一个标记位跟踪当前是否正在发送消息。

  1. 先定义一个简单的结构体来存储请求参数(比类更简洁):
struct MessageRequest {
    let message: [String: Any]
    let replyHandler: (([String: Any]) -> Void)?
    let errorHandler: ((Error) -> Void)?
}
  1. 初始化队列、请求数组和状态标记:
private let messageQueue = DispatchQueue(label: "com.your.app.wc.message.queue")
private var pendingRequests: [MessageRequest] = []
private var isCurrentlySending = false
  1. 封装入队和处理逻辑:
// 对外暴露的方法,用来添加消息请求
func enqueueMessage(_ message: [String: Any], replyHandler: (([String: Any]) -> Void)? = nil, errorHandler: ((Error) -> Void)? = nil) {
    messageQueue.async {
        // 把请求加入待处理数组
        self.pendingRequests.append(MessageRequest(message: message, replyHandler: replyHandler, errorHandler: errorHandler))
        // 如果当前没有正在发送的消息,就处理下一个请求
        if !self.isCurrentlySending {
            self.processNextRequest()
        }
    }
}

// 内部方法,处理下一个待发送的请求
private func processNextRequest() {
    guard !pendingRequests.isEmpty else {
        // 没有待处理请求,标记状态为空闲
        isCurrentlySending = false
        return
    }
    
    isCurrentlySending = true
    // 取出最早的请求(FIFO)
    let nextRequest = pendingRequests.removeFirst()
    
    // 调用sendMessage
    WCSession.default.sendMessage(nextRequest.message, replyHandler: { reply in
        // 回到串行队列处理回调和下一个请求
        self.messageQueue.async {
            // 执行回调(注意如果需要更新UI,要切回主队列)
            DispatchQueue.main.async {
                nextRequest.replyHandler?(reply)
            }
            // 处理完当前请求,继续下一个
            self.processNextRequest()
        }
    }, errorHandler: { error in
        self.messageQueue.async {
            DispatchQueue.main.async {
                nextRequest.errorHandler?(error)
            }
            self.processNextRequest()
        }
    })
}

方法二:OperationQueue串行执行

利用OperationQueue的maxConcurrentOperationCount属性,把它设为1,就能自动实现串行执行。每个sendMessage请求包装成一个BlockOperation,注意要让Operation等待sendMessage的异步回调完成,不然队列会立刻执行下一个Operation。

private let messageOperationQueue = OperationQueue()

// 在初始化方法里配置队列
init() {
    messageOperationQueue.maxConcurrentOperationCount = 1 // 只允许同时执行一个Operation
}

func enqueueMessage(_ message: [String: Any], replyHandler: (([String: Any]) -> Void)? = nil, errorHandler: ((Error) -> Void)? = nil) {
    let messageOperation = BlockOperation { [weak self] in
        guard let self = self else { return }
        
        // 用信号量等待sendMessage的异步回调完成
        let semaphore = DispatchSemaphore(value: 0)
        
        WCSession.default.sendMessage(message, replyHandler: { reply in
            // 回调切回主队列(如果要更新UI的话)
            DispatchQueue.main.async {
                replyHandler?(reply)
            }
            semaphore.signal() // 通知信号量,请求完成
        }, errorHandler: { error in
            DispatchQueue.main.async {
                errorHandler?(error)
            }
            semaphore.signal()
        })
        
        semaphore.wait() // 阻塞当前Operation,直到回调触发
    }
    
    messageOperationQueue.addOperation(messageOperation)
}

两种方法对比

  • 方法一更轻量,不需要用到信号量,逻辑更直观,适合大多数场景。
  • 方法二利用Operation的特性,如果后续需要给请求添加依赖、取消等操作,会更灵活。

不管用哪种方法,都能实现你要的FIFO排队效果,避免多次sendMessage同时调用导致的问题。

内容的提问来源于stack exchange,提问作者Reinhard Männer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:35:52