javascriptreact-nativepromisebluetooth-lowenergyreact-native-ble-plx

Issue processing async function in a Bluetooth manager component listener function


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")
                    }
                }
            )

Solution

  • @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")
                        }
                    }
                )