2

I am writing an Angular2 based mobile app, using Typescript with the Nativescript runtime, but am facing some issues with Promises. I have a HomeComponent, from which I would like to be able to call various Bluetooth functions. These require human interaction (like selecting the device) and wait periods for the Bluetooth scan, so they take several seconds to complete.

I want to abstract those methods as promises, so I have done this:

HomeComponent.ts:

bluetoothAdd() {
    this.isScanning = true;
    this._ble.scan().then(
        // Log the fulfillment value
        function (val) {
            this.isScanning = false; //hide activity indicator
            this.Connect(this.ble._peripheral);
        })
        .catch(
        function (reason) {
            this.isScanning = false; //hide activity indicator
            console.log('Handle rejected promise (' + reason + ') here.');
        });
}

BluetoothUtils.ts (this is imported above as this._ble):

var devices: Array<string>;
var _peripheral: any;

export function scan() {
    return new Promise((resolve, reject) => {
        bluetooth.hasCoarseLocationPermission().then(
            (granted) => {
                if (!granted) {
                    bluetooth.requestCoarseLocationPermission();
                } else {
                    bluetooth.startScanning({
                        serviceUUIDs: ["133d"],
                        seconds: 4,
                        onDiscovered: (peripheral) => {
                            console.log("Periperhal found with UUID: " + peripheral.UUID);
                        }
                    }).then(() => {
                        console.log("scanning complete");

                    }, (err) => {
                        console.log("error while scanning: " + err);
                    });
                }
            });
    });
}

Having stepped through my app with the debugger, when bluetoothAdd() is called in my HomeComponent, my scan() function in BluetoothUtils works as expected and in one scenario, executes the console.log("error while scanning: " + err); line, saying:

error while scanning: Bluetooth is not enabled

However, neither the then or the catch parts of the HomeComponent this._ble.scan() promise are executed? Why is this? Can you compound Promises (i.e. a promise in a promise) as I am trying to do? or any ideas how ot debug this further?

George Edwards
  • 8,979
  • 20
  • 78
  • 161
  • Avoid the [`Promise` constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! Notice you're not even calling `resolve` or `reject` anywhere, so that's why the promise that `.scan()` returns never settles. – Bergi May 28 '16 at 18:47

3 Answers3

1

There's a few problems.

1/ You're using an anti pattern. There's no need to create a new promise in scan since all you're dealing with are promises.

2/a) If you want to use the anti pattern, you need to call the resolve and reject functions, i.e. in the current situation the new promise is never resolved nor rejected, and therefore the "main" promise in scan is never resolved or rejected either.

2/b) A better solution: simply return the promises already created. If you don't return they will become separate promise branches and won't influence the "main" promise branch in scan, i.e.:

export function scan() {
    return bluetooth.hasCoarseLocationPermission().then(
        (granted) => {
            if (!granted) {
                return bluetooth.requestCoarseLocationPermission();
            } else {
                return bluetooth.startScanning({
                    serviceUUIDs: ["133d"],
                    seconds: 4,
                    onDiscovered: (peripheral) => {
                        console.log("Periperhal found with UUID: " + peripheral.UUID);
                    }
                }).then(() => {
                    console.log("scanning complete");

                }, (err) => {
                    console.log("error while scanning: " + err);
                });
            }
        });
}

Note the 3 added returns: one at the top of scan and then before bluetooth.requestCoarseLocationPermission and bluetooth.startScanning

If you want to know what other promise anti patterns exist, here's a great resource

Creynders
  • 4,573
  • 20
  • 21
  • Thanks for this. So if I follow your suggestion 2b, what would my HomeComponent code look like? – George Edwards May 28 '16 at 14:07
  • Exactly the same. Unless `this.Connect` also returns a promise _and_ you want it to be handled in the same promise branch, in which case you should `return` it too. – Creynders May 28 '16 at 14:09
1

Since you are using typescript, please don't wait await/async anymore. It is much clearer especially when you have multiple promises.

async function bluetoothAdd() {
    this.isScanning = true;
    try {
        const val = await this._ble.scan();
        this.Connect(this.ble._peripheral);
    } catch (reason) {
        console.log('Handle rejected promise (' + reason + ') here.');
    }finally{
        this.isScanning = false;
    }
}


//Bluetoo.....ts
var devices: Array<string>;
var _peripheral: any;

export async function scan() {
    const granted = await bluetooth.hasCoarseLocationPermission();
    if(!granted){
        return bluetooth.requestCoarseLocationPermission();
    }else{
        try {
            const val = await bluetooth.startScanning({
                    serviceUUIDs: ["133d"],
                    seconds: 4,
                    onDiscovered: (peripheral) => {
                        console.log("Periperhal found with UUID: " + peripheral.UUID);
                    }
                });
            console.log("scanning complete");
            return val;
        } catch (err) {
            // you can rethrow err here such that user `bluetoothAdd` will recieve it/
            console.log("error while scanning: " + err);
        }
    }
}
Zen
  • 5,065
  • 8
  • 29
  • 49
0

It looks like the code in the first then does not return anything i.e. you are not returning a promise. I would expect to see stuff like return bluetooth.startScanning(..). You certainly can nest promises, but it is more readable to chain them. I.e. move the then for console.log up to the same level of nesting as the previous then.

Andrew
  • 5,215
  • 1
  • 23
  • 42