Ionic v4+Angular中BLE操作顺序执行问题求助
嘿,这问题我太熟了——嵌套的then和订阅确实会把代码搞成“回调地狱”,用async/await是正道,但你之前的用法把await和then混在一起,而且核心问题是你的自定义函数可能没返回可等待的Promise,所以await根本起不到“等待上一步完成”的作用。我给你一步步理顺:
第一步:把每个BLE操作函数改成返回Promise
Angular的BLE插件大多基于RxJS Observable,我们需要把这些Observable转换成Promise,这样await才能正确等待它们完成。同时要把扫描到的设备、连接信息存在类的属性里,供后续函数使用。
1. 改造扫描函数myScan
async myScan(): Promise<void> { // 用RxJS的pipe+first过滤出目标设备,转成Promise const targetDevice = await BLE.scan([], 5).pipe( // 替换成你的设备匹配逻辑,比如名称/UUID filter(device => device.name === "RaspberryPi-BLE"), first() // 找到第一个匹配的设备就停止 ).toPromise(); // 把设备信息存在类属性里,供后续连接用 this.targetDevice = targetDevice; console.log("扫描到目标设备:", targetDevice); }
注意:Angular 12+版本里
toPromise()已经废弃,改用firstValueFrom或lastValueFrom,比如:import { firstValueFrom } from 'rxjs'; // ... const targetDevice = await firstValueFrom( BLE.scan([], 5).pipe(filter(device => device.name === "RaspberryPi-BLE"), first()) );
2. 改造连接函数myConnect
依赖myScan保存的this.targetDevice,同样转成Promise:
async myConnect(): Promise<void> { if (!this.targetDevice) { throw new Error("请先扫描并找到目标设备!"); } // 等待连接成功 await BLE.connect(this.targetDevice.id).pipe(first()).toPromise(); // 保存服务和特征UUID,供后续通知/发消息用 this.serviceUUID = "你的BLE服务UUID"; this.txCharUUID = "发送数据的特征UUID"; this.rxCharUUID = "接收通知的特征UUID"; console.log("设备连接成功"); }
3. 改造通知激活函数myNotification
这里我们只需要等待通知订阅成功,后续持续接收数据的逻辑可以单独处理:
async myNotification(): Promise<void> { return new Promise((resolve, reject) => { BLE.startNotification( this.targetDevice.id, this.serviceUUID, this.rxCharUUID ).subscribe( (data) => { // 这里处理收到的通知数据,比如转成字符串 const receivedText = this.bytesToString(data); console.log("收到通知:", receivedText); // 确认通知激活成功,resolve让后续函数继续执行 resolve(); }, (error) => { console.error("激活通知失败:", error); reject(error); } ); }); }
4. 改造消息发送函数myMessage
async myMessage(text: string): Promise<void> { // 把字符串转成BLE需要的字节数组(根据你的设备协议调整) const data = this.stringToBytes(text); await BLE.write( this.targetDevice.id, this.serviceUUID, this.txCharUUID, data ).toPromise(); console.log(`消息发送完成: ${text}`); } // 辅助函数:字符串转字节数组 private stringToBytes(str: string): number[] { const bytes = []; for (let i = 0; i < str.length; i++) { bytes.push(str.charCodeAt(i)); } return bytes; } private bytesToString(bytes: number[]): string { return String.fromCharCode(...bytes); }
第二步:用async函数按顺序调用
现在所有函数都返回Promise了,你可以在一个异步函数里用await按顺序执行,再也不用嵌套:
async runBLEWorkflow() { try { // 按顺序执行,每一步都等待上一步完成 await this.myScan(); await this.myConnect(); await this.myNotification(); // 发送多条消息,同样顺序执行 await this.myMessage('Text 1'); await this.myMessage('03020000'); await this.myMessage('010100'); await this.myMessage('020200' + this.random.toString(16)); console.log("所有BLE操作完成!"); } catch (error) { // 统一处理整个流程的错误 console.error("BLE流程出错:", error); // 出错后记得断开连接(如果已连接) if (this.targetDevice) { BLE.disconnect(this.targetDevice.id); } } }
为什么你之前的用法不对?
你把await和then嵌套在一起,相当于又回到了回调地狱的写法,而且如果你的myConnect等函数没返回Promise,await根本无法等待它完成。现在的写法把每个步骤拆成独立的、返回Promise的函数,在一个async函数里用await依次调用,逻辑清晰,错误也能统一捕获。
最后记得替换代码里的设备名称、服务/特征UUID,还有数据转换逻辑要匹配你的树莓派BLE协议哦!
内容的提问来源于stack exchange,提问作者Amaury Laroze
相关产品推荐
相关产品推荐

