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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:37:40