如何将蓝牙独立回调响应关联到Promise.resolve实现sendData方法Promisify
实现方案
由于蓝牙响应是全局监听器统一接收,你需要用队列/映射表存储每次请求的resolve/reject回调,收到响应后匹配对应回调执行即可,分两种常见场景实现:
场景1:设备响应按请求顺序返回(绝大多数低功耗蓝牙设备适用)
蓝牙设备通常会按请求发送的先后顺序返回响应,用队列存储回调即可:
- 连接设备时初始化回调队列,全局只注册一次响应监听器
device.connect().then(device => { // 存储待响应的请求回调队列 device._responseQueue = []; // 全局响应监听器,收到响应后执行队列最前面的回调 device.registerResponseListener((data) => { if (device._responseQueue.length) { const { resolve } = device._responseQueue.shift(); resolve(data); } }); // 可选:添加断开连接监听,清空所有待处理请求避免内存泄漏 device.registerDisconnectListener(() => { device._responseQueue.forEach(({ reject }) => reject(new Error('设备已断开连接'))); device._responseQueue = []; }); })
- 封装带超时的Promisified
sendData方法
const sendData = (device, data, timeout = 10000) => { return new Promise((resolve, reject) => { const requestItem = { resolve, reject }; // 先把当前请求的回调加入队列 device._responseQueue.push(requestItem); // 超时处理,避免请求丢失导致Promise永久pending const timer = setTimeout(() => { const index = device._responseQueue.indexOf(requestItem); if (index > -1) device._responseQueue.splice(index, 1); reject(new Error('请求超时,未收到设备响应')); }, timeout); try { device.write(data); } catch (err) { // 发送失败清理资源 clearTimeout(timer); const index = device._responseQueue.indexOf(requestItem); if (index > -1) device._responseQueue.splice(index, 1); reject(err); } }); }
场景2:设备响应带请求唯一标识(支持并发请求无错配)
如果你的蓝牙协议里定义了请求ID字段,响应会携带对应请求的ID,可以用映射表存储回调,不用依赖顺序,更稳定:
- 连接设备时初始化映射表
device.connect().then(device => { // 用请求ID作为key存储回调 device._responseMap = new Map(); device.registerResponseListener((data) => { const reqId = data.requestId; // 取响应里携带的请求ID if (device._responseMap.has(reqId)) { const { resolve, timer } = device._responseMap.get(reqId); clearTimeout(timer); device._responseMap.delete(reqId); resolve(data); } }); })
- 封装
sendData方法,每次生成唯一ID携带在请求中
const sendData = (device, data, timeout = 10000) => { return new Promise((resolve, reject) => { // 生成唯一请求ID const reqId = Math.random().toString(36).slice(2); // 把请求ID加入发送的数据中 const payload = { ...data, requestId: reqId }; const timer = setTimeout(() => { device._responseMap.delete(reqId); reject(new Error('请求超时')); }, timeout); device._responseMap.set(reqId, { resolve, timer }); try { device.write(payload); } catch (err) { clearTimeout(timer); device._responseMap.delete(reqId); reject(err); } }); }
注意事项
- 必须添加超时逻辑,避免设备无响应时内存泄漏
- 若存在并发发送请求的场景,优先选择带请求ID的实现方案
- 设备断开、报错时要清空所有待处理的Promise回调
内容的提问来源于stack exchange,提问作者T3db0t
相关产品推荐
相关产品推荐

