ste
ste

Reputation: 3259

Javascript: return a nested promise from a function

I'm writing an application in React (react-native actually, but it's not relevant).

I'm using a bluetooth module that perform different actions in the form of chained promises, something like this:

  writeData(data) {
    this.manager.startDeviceScan(null, null, (error, device) => {
      if (error) {
        this.store.dispatch({type: "SET_ERROR", payload: error.message})
        return
      }

      if (device.name === 'MyDevice') {
        this.store.dispatch({type: "SET_STATUS", payload: "Device found, stopping device scan...\n"})

        this.manager.stopDeviceScan();

        device.connect().then((device) => {
          this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
          return device.discoverAllServicesAndCharacteristics()
        }).then((device) => {
          device.writeCharacteristicWithoutResponseForService(
            data.serviceId,
            data.charId,
            data.dataToWrite
          ).then(res => {
            this.store.dispatch({type: "SET_STATUS", payload: `${data.message}!\n`})

            device.cancelConnection().then(res => {
              this.store.dispatch({type: "SET_STATUS", payload: 'Action dispatched, closing connection\n'})
            }).catch(error => {
              this.store.dispatch({type: "SET_ERROR", payload: error.message})
            })
          }).catch(error => {
            this.store.dispatch({type: "SET_ERROR", payload: error.message})
          })
        }).catch((error) => {
          this.store.dispatch({type: "SET_ERROR", payload: error.message})
        });
      }
    });
  }

Now, to have more flexibility in my application I would like to handle errors and success in the local component where I call this function. So, instead of having this function in a separate file and using it in various component like this:

  BleApi.writeData({
    serviceId: "ee733136-2de5-4e04-ae81-xxxxxxxxx",
    charId: "cde744c6-2966-4b08-84d2-xxxxxxxxx",
    dataToWrite: btoa(`${index};${program};0`),
    message: "Program has been successfully added.",
  })

I would like to return the promise from the connection module, so I can have the then/catch in the local component:

device.connect().then((device) => {
  this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
  return device.discoverAllServicesAndCharacteristics()
}).then((device) => {
  // this is the function I would like to return:
  return device.writeCharacteristicWithoutResponseForService(
    data.serviceId,
    data.charId,
    data.dataToWrite
  )

and then wherever I call this function:

  BleApi.writeData({
    serviceId: "ee733136-2de5-4e04-ae81-xxxxxxxxx",
    charId: "cde744c6-2966-4b08-84d2-xxxxxxxxx",
    dataToWrite: btoa(`${index};${program};0`),
    message: "Program has been successfully added.",
  }).then(res => {}).catch(error => {}) // now I can handle the result/errors here

but with this code I get TypeError: Cannot read property 'then' of undefined

How can I fix this?

EDIT: error in copying the function (it was BleApi both time), plus I want to clarify something that I forgot: I know that BleApi.writeData returns a promise because that's the functon I'm writing. I want it to return a promise and right now it returns undefined. Sorry for not specifying this before

EDIT 2: I guess I missed another piece of relevant information. In the function writeData everything is wrapped around the function startDeviceScan of the bluetooth module, and the way I wrote discoverAllServicesAndCharacteristics is entirely taken form the documentation of the module: https://polidea.github.io/react-native-ble-plx/

Upvotes: 2

Views: 106

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1073978

I assume Ble.writeData/BleApi.writeData is the function where your device.connect() code is located? If so, the problem is that you're not returning the promise chain. It's hard to give you a solid answer without more context for the code you've provided, but it would be something like:

writeData() {
    return device.connect()
    .then((device) => {
      this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
      return device.discoverAllServicesAndCharacteristics();
    })
    .then((device) => {
      return device.writeCharacteristicWithoutResponseForService(
        data.serviceId,
        data.charId,
        data.dataToWrite
      );
    });
}

(I'm using method syntax there, assuming this is within an object literal or a class.)

...except I suspect you meant for this line:

return device.discoverAllServicesAndCharacteristics();

to be

return device.discoverAllServicesAndCharacteristics().then(() => device);

...since your next then handler expects to get device. If so, then:

writeData() {
    return device.connect()
    .then((device) => {
      this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
      return device.discoverAllServicesAndCharacteristics().then(() => device);
    })
    .then((device) => {
      return device.writeCharacteristicWithoutResponseForService(
        data.serviceId,
        data.charId,
        data.dataToWrite
      );
    });
}

But note that since you're transpiling with React Native, you can use an async function so you can write your logical flow with standard flow control constructs rather than .then and .catch handlers:

async writeData() {
    const device = await device.connect();
    this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
    await device.discoverAllServicesAndCharacteristics();
    return device.writeCharacteristicWithoutResponseForService(
        data.serviceId,
        data.charId,
        data.dataToWrite
    );
}

Upvotes: 2

Related Questions