I am having issues running some asynchronous code in a bluetooth RN app.
I am trying to create a listener function that does the following: connect to a device (using an async function), log that I have connected, then disconnect from the device (using an async function).
This listener function is provided as a parameter to a bluetooth low energy (ble) device scan function:
// Relevant Function Prototypes --------------------------------------------------
// startDeviceScan() -> This scans for devices and runs a listener function on each
// device it scans.
bleManager.startDeviceScan(
UUIDs: ?Array<UUID>,
options: ?ScanOptions,
listener: (error: ?Error, scannedDevice: ?Device) => void // <- this listener function
)
// connectToDevice() -> This connects to a device scanned by the bleManager in the
// listener function given to startDeviceScan()
bleManager.connectToDevice(
deviceIdentifier: DeviceId,
options: ?ConnectionOptions,
): Promise<Device>
// My code ------------------------------------------------------------------------
// Scans nearby ble devices advertising and runs a listener function that has the
// connection error status and device id as given parameters.
// **This function triggers on a Button onPress
const handleStartScanning = async () => {
try {
bleManager.startDeviceScan(
['00001200-0000-1000-8000-00805f9b34fb'], // the service UUID I am scanning for
{ allowDuplicates: true }, // I allow to duplicates to continuously reconnect to devices
async (error, device) => {
// get services
let services = device.serviceUUIDs // get list of the service UUIDs on device
// make sure services not null and out service UUID is included
if (services && services.includes('00001200-0000-1000-8000-00805f9b34fb')) {
// log the scanned device's name, rssi, and its service UUIDs
console.log("Scanned a device with name: " + device.name + " | " + device.rssi)
console.log("Services:", services)
await bleManager.connectToDevice(device.id) // <- *****ISSUE HERE*****
console.log("Connected to device") // <- *****THIS NEVER PRINTS*****
// run some more async code here once i'm connected to the device
await bleManager.cancelDeviceConnection(device.id)
}
}
)
} catch (error) {
console.log('Could not start scanning for devices', { error })
}
}
I don't understand why even though I made the listener function async and awaited the 2 asynchronous function calls in the listener, the Connected to Device
log never prints. That means that the listener never gets executed past the await bleManager.connectToDevice(device.id)
async function call
Here is my console log:
Note that Possible Unhandled Promise Rejection (id: 150): BleError: Operation was cancelled
type error logs repeatedly after the snippet I posted below. Sometimes the log goes back to scanning in some devices but always comes back to the promise rejections. The Connected to device
console.log never prints.
[Thu Oct 01 2020 22:59:42.385] LOG Scanned a device with name: Android | -43
[Thu Oct 01 2020 22:59:42.387] LOG Services: ["00001200-0000-1000-8000-00805f9b34fb"]
[Thu Oct 01 2020 22:59:42.388] LOG Scanned a device with name: Android | -43
[Thu Oct 01 2020 22:59:42.388] LOG Services: ["00001200-0000-1000-8000-00805f9b34fb"]
[Thu Oct 01 2020 22:59:42.487] LOG Scanned a device with name: Android | -44
[Thu Oct 01 2020 22:59:42.491] LOG Services: ["00001200-0000-1000-8000-00805f9b34fb"]
[Thu Oct 01 2020 22:59:42.492] LOG Scanned a device with name: Android | -44
[Thu Oct 01 2020 22:59:42.492] LOG Services: ["00001200-0000-1000-8000-00805f9b34fb"]
[Thu Oct 01 2020 22:59:42.514] WARN Possible Unhandled Promise Rejection (id: 150):
BleError: Operation was cancelled
construct@[native code]
_construct@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:23889:28
Wrapper@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:23844:25
construct@[native code]
_createSuperInternal@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:117317:322
BleError@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:117330:26
parseBleError@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:117368:30
_callPromise$@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:117632:51
tryCatch@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:24712:23
invoke@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:24885:32
tryCatch@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:24712:23
invoke@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:24785:30
http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:24797:21
tryCallOne@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:26784:16
http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:26885:27
_callTimer@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:30324:17
_callImmediatesPass@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:30363:17
callImmediates@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:30580:33
__callImmediates@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:2630:35
http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:2416:34
__guard@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:2613:15
flushedQueue@http://10.0.0.122:8081/index.bundle?platform=ios&dev=true&minify=false:2415:21
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]
SOLUTION UPDATE: Big thanks to Jaromanda X for providing solution!
The fix was to add try catches around the connectToDevice() and cancelDeviceConnection() async functions as they were getting rejected and the the listener would return (hence why the 'Connected to Device' log never printed).
bleManager.startDeviceScan(
['00001200-0000-1000-8000-00805f9b34fb'],
{ allowDuplicates: true },
async (error, device) => {
// get services
let services = device.serviceUUIDs
// check if there are services being advertised
if (services && services.includes('00001200-0000-1000-8000-00805f9b34fb')) {
console.log("Scanned a device with name: " + device.name + " | " + device.rssi)
console.log("Services:", services)
try {
await bleManager.connectToDevice(device.id)
} catch {
console.log("Could not connect")
}
console.log("Connected to device: ", device.name)
// run some more async code once i'm connected to the device
try {
await bleManager.cancelDeviceConnection(device.id)
} catch {
console.log("Could not disconnect")
}
// await bleManager.connectToDevice(device.id)
//console.log("Connected to device")
//await bleManager.cancelDeviceConnection(device.id)
//console.log("Disconnected from device")
}
}
)
@Jaromanda X provided the solution:
The fix was to add try catches around the connectToDevice() and cancelDeviceConnection() async functions as they were getting rejected and the the listener would return (hence why the 'Connected to Device' log never printed).
bleManager.startDeviceScan(
['00001200-0000-1000-8000-00805f9b34fb'],
{ allowDuplicates: true },
async (error, device) => {
// get services
let services = device.serviceUUIDs
// check if there are services being advertised
if (services && services.includes('00001200-0000-1000-8000-00805f9b34fb')) {
console.log("Scanned a device with name: " + device.name + " | " + device.rssi)
console.log("Services:", services)
try {
await bleManager.connectToDevice(device.id)
} catch {
console.log("Could not connect")
}
console.log("Connected to device: ", device.name)
// run some more async code once i'm connected to the device
try {
await bleManager.cancelDeviceConnection(device.id)
} catch {
console.log("Could not disconnect")
}
// await bleManager.connectToDevice(device.id)
//console.log("Connected to device")
//await bleManager.cancelDeviceConnection(device.id)
//console.log("Disconnected from device")
}
}
)