setTimeout function triggers callback twice

Viewed 744

I have a write function which basically add data to queue and sends data every 500ms and i have used couple of setTimeouts to write data as below. And Analysing logs i suspsect setTimeout has been called twice, i want to know is it even possible? and what could be the possible fix?


import BleManager from 'react-native-ble-manager';

const BleManagerModule = NativeModules.BleManager;
const bleManagerEmitter = new NativeEventEmitter(BleManagerModule);

class bleHandler {
constructor() {
    this.writeQueue = [];
    bleManagerEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', this.handleUpdateValueForCharacteristic);
}

//this function will be called when ble receives data from peripheral.
handleUpdateValueForCharacteristic(data){
   // do something with data
   //send ack that data is received.
   this.writeData("AB:CD:EF:GH:IJ", " Some Important Message text ");
}

 writeData(peripheralID, data) {
    console.log("Adding data to Queue " + data);
    if (this.writeQueue.length == 0) {
      this.writeQueue.push(data);
      setTimeout(() => this.sendData(peripheralID, data), 500);
    } else
      this.writeQueue.push(data);

    console.log("New Queue Length = " + this.writeQueue.length);
  }

  sendData = (peripheralID, data) => {
    if (data.length > 0 && peripheralID && this.writeQueue.length > 0) {
      var byteBuf = this.formatData(data);
console.log("============" + slicedData + " and length = " + slicedData.length + " at Date = " + new Date().valueOf());
console.log("Writing Message : " + byteBuf );
      this.handleWriteWithoutResponse(peripheralID, UUID, CHAR, byteBuf).then(() => {
        console.log("success writing data ");

        data = data.substring(PACKET_SIZE - 5);
        if (data.length > 0) {
          setTimeout(() => {
            this.writeQueue[0] = data;
            this.sendData(peripheralID, data)
          }, 500);
        } else {
          this.writeQueue.shift();
          setTimeout(() => {
            if (this.writeQueue.length > 0)
              this.sendData(peripheralID, this.writeQueue[0]);
          }, 500);
        }
      }).catch((error) => {
        console.log("error writing data ", error);
      });
    }
  }
}
const bleModule = new bleHandler();
export default bleModule;

Logs

11:13:28 log Adding data to Queue " Some Important Message text "
11:13:28 log New Queue Length = 1
11:13:28 log ============" Some Important Message text " and length = 28 at Date = 1597680808275
11:13:28 log Writing Message : 9876543211223A2243222C202233223A7B2241636B223A223230302212345
11:13:28 log ============" Some Important Message text " and length = 28 at Date = 1597680808276
11:13:28 log Writing Message : 9876543211223A2243222C202233223A7B2241636B223A223230302212345
11:13:28 log success writing data 
11:13:28 log success writing data 

Now if you check the logs in IOS, i have seen that send data is called twice within (1597680808276 - 1597680808275) 1 ms intervel and it was called from setTimeout function. Is there an issue with setTimeout in js ? or may be react-native issue or safari issue ? and how could i fix this issue. I a have seen a similar issue with been fixed in nodejs few years ago.

Note: Im using react-native-ble-manager for sending / receiving data.

1 Answers

The root cause of the issue is due to the function sendData calling itself.

sendData = (peripheralID, data) => {
  if (data.length > 0 && peripheralID && this.writeQueue.length > 0) {
    // ... 
    if (data.length > 0) {
      setTimeout(() => {
      // ...
      this.sendData(peripheralID, data)                      ---> Recursive call
      }, 500);
    } else {
      // ...
      setTimeout(() => {
        if (this.writeQueue.length > 0)
          this.sendData(peripheralID, this.writeQueue[0]);   ---> Recursive call
        }, 500);
      }
    // ...
  }

The issue of multiple calls to sendData may be resolved by removing the recursion

Here's an example where two timer callbacks, both scheduled to occur 500ms later, arrive in rapid succession:

function example(a) {
    console.log("example:", a, "at", Date.now());
    if (a <= 3) {
        setTimeout(() => {
            example(a * 2);
        }, 500);
    }
}
// Schedule the calls
example(2);
example(3);

// Keep the thread busy
const end = Date.now() + 600;
while (Date.now() < end) {
}

Note how

example: 4 at 1597923771329
example: 6 at 1597923771330

are right next to each other in terms of time.

Related