1

I am sending data to a Bluetooth device, and the responses are handled by a listener that's set up during the connection process:

device.connect().then(device => {
  device.registerResponseListener((data) => {
    // handle response
  }
}

I have a separate function that sends data to the device:

const sendData = (device, data) => {
  device.write(data);
}

My question is, how can I Promisify this code? I'd like to be able to do

const sendData = (device, data) => {
  return new Promise((resolve, reject) => {
    device.write(data);
    // resolve...?
  });
}

But how do I get the resolve into the Bluetooth response listener?

T3db0t
  • 3,491
  • 4
  • 28
  • 45
  • You can probably use async/await – Kenneth Lew Oct 17 '21 at 20:15
  • 2
    Why do you need to promisify this function? What problem are you solving? – VLAZ Oct 17 '21 at 20:17
  • 3
    Is there an opposite of `registerResponseListener` like `unregisterResponseListener`? Any documentation of this API? – trincot Oct 17 '21 at 20:22
  • You'll need [a queue](https://stackoverflow.com/a/60517241/1048572) of `resolve` functions – Bergi Oct 17 '21 at 20:37
  • @VLAZ Because I need to be able to chain a sequence of requests. – T3db0t Oct 17 '21 at 21:43
  • @trincot It's this https://dotintent.github.io/react-native-ble-plx/ —but this question is not about the specific API, it's about how to handle the situation in general – T3db0t Oct 17 '21 at 21:43
  • In the situation in general you would read the documentation and see what it offers, so that you would not re-event the wheel and make use of the best services available. Can you point to the documentation of `registerResponseListener`? I don't find it there. – trincot Oct 17 '21 at 21:46
  • @T3db0t the solution to this in general is *not* to use a single listener (set up during connection) to handle all the responses. In the ideal case, the library you're using does natively support promises (and in fact the library you linked *does*!), if their API is suboptimal you have to work with what you got (and e.g. implement queuing or request/response-linking yourself, like in the answer I linked). – Bergi Oct 17 '21 at 21:51
  • The API is promise-based but not in the way that I need for this. Responses to a write come ONLY from `monitorCharacteristicForService` (I used a simpler name in my example). I'll investigate the possibility of using the request queue—would it warrant a full answer here? – T3db0t Oct 17 '21 at 22:11
  • @T3db0t From a quick glance, there's [`writeCharacteristicWithResponseForDevice`](https://dotintent.github.io/react-native-ble-plx/#blemanagerwritecharacteristicwithresponsefordevice) and also `readCharacteristicForService` which both return a promise for the response – Bergi Oct 17 '21 at 22:14
  • @Bergi as I said, in this particular case, the ONLY way to receive the response is via the listener. All the promise does is return the Characteristic I was writing to. – T3db0t Oct 17 '21 at 22:17
  • @T3db0t What will happen if you call `write()` multiple times before the response is received? How can you match the response to the respective `sendData` call? – Bergi Oct 17 '21 at 23:36
  • @Bergi Exactly what I’m concerned about – T3db0t Oct 18 '21 at 12:07
  • @T3db0t Is there a request id in the response? Are the responses just in the same order as the writes? – Bergi Oct 18 '21 at 14:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/238282/discussion-between-t3db0t-and-bergi). – T3db0t Oct 18 '21 at 17:37
  • See also https://stackoverflow.com/q/61937887/1048572 – Bergi Oct 20 '21 at 01:50

3 Answers3

0

I don't know what API you're using but you can try BluetoothRemoteGATTCharacteristic API. It has writeValueWithResponse method which return Promise.

https://developer.mozilla.org/en-US/docs/Web/API/BluetoothRemoteGATTCharacteristic

Ivan
  • 1
  • 2
  • This question is not about any API in particular, it's about what to do in this situation in general. – T3db0t Oct 17 '21 at 21:44
0

If I understood you correctly then you can do it like this

const sendData = async (device, data) => {
  const response = await device.write(data);
  await Promise.all(device.responseListeners.map(listener => listener(response)))
}
Ivan
  • 1
  • 2
0

The best possible solution in this case, while still not ideal, was to store the resolve function in variable at a higher scope:

var sendDataResolve;

device.connect().then(device => {
  device.registerResponseListener((data) => {
    sendDataResolve(data);
  }
}

const sendData = (device, data) => {
  return new Promise((resolve, reject) => {
    sendDataResolve = resolve;
    device.write(data);
  });
}

...

sendData(device, "data")
.then(result => {
  console.log("Got result",result);
});

The caveat is that Promise resolutions are NOT guaranteed to be tied correctly to the original request. This ONLY works with one request at a time.

T3db0t
  • 3,491
  • 4
  • 28
  • 45