如何分配在异步函数的同步范围中定义的变量?

时间:2022-10-30 08:59:46

I am trying to write a GUI frontend that uses a service to get data about the system. I am using a net.Socket for the client end of this. I want to be able to access certain variables assigned in the data event handlers in other modules but the assignment does not stay after that callback function finishes.

我正在尝试编写一个GUI前端,它使用一个服务来获取关于系统的数据。我在用网。客户端套接字。我希望能够访问在其他模块中的数据事件处理程序中分配的某些变量,但是在回调函数结束后,分配不会继续。

Problematic code:

有问题的代码:

client.on('data', (data) => {
      var array = [...data];
      array.splice(0,2);
      for (var i=0;i<array.length;i++) {
        dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
      }
      console.log(dataInBuffer);
      if (dataInBuffer.startsWith('batStat')) {
        let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
        module.exports.hasBattery = lastBatteryJSON.hasBattery == 'true';
        module.exports.isCharging = lastBatteryJSON.isCharging == 'true';
        module.exports.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
      }
      dataInBuffer = '';
    });

Those three exported variable assignments don't actually work, the variables always either stay undefined or their default values outside of the function. I tried using a Promise to solve this problem but got the same result. I'm at a loss and I can't find any other questions or forum posts that solve this problem.

这三个导出的变量赋值实际上不起作用,变量要么是未定义的,要么是在函数之外的默认值。我试着用一个承诺来解决这个问题,但是得到了同样的结果。我不知所措,我找不到任何其他的问题或论坛帖子来解决这个问题。

EDIT I do not have the option of moving the code that depends on those variables into the callback. In order to do that I would have to wait for the data every frame and flood the server as a result.

编辑我没有将依赖于这些变量的代码移到回调的选项。为了做到这一点,我将不得不等待每一帧的数据,并最终淹没服务器。

2 个解决方案

#1


1  

As apple commented; you can export an object and mutate it every time you receive data:

苹果说;您可以导出一个对象,并在每次接收数据时进行变异:

const data = {};
client.on('data', (data) => {
  var array = [...data];
  array.splice(0, 2);
  for (var i = 0; i < array.length; i++) {
    dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
  }
  console.log(dataInBuffer);
  if (dataInBuffer.startsWith('batStat')) {
    let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
    //mutate the data object
    data.hasBattery = lastBatteryJSON.hasBattery == 'true';
    data.isCharging = lastBatteryJSON.isCharging == 'true';
    data.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
  }
  dataInBuffer = '';
});
//export the data object
module.exports.batteryData = data;

Or as CertainPerformance answered you can have the caller decide when to ask for the information and provide a promise.

或者根据特定的表现,你可以让打电话的人决定什么时候询问信息并做出承诺。

Here is an extended version of CertainPerformance answer that listens to error as well so a promise can be rejected and cleans up the event listeners when promise is resolved or rejected:

下面是一个扩展版本的确定性能回答,它也会侦听错误,因此当承诺被解决或拒绝时,可以拒绝承诺并清理事件侦听器:

//wrapper for client.on to add and remove event listeners
const listeners = (function(){
  var listenerCounter = -1;
  const listeners = [];
  const triggerEvent = event => data =>{
    listeners.filter(
      listener=>listener[2] === event
    ).forEach(
      listener=>listener[1](data)
    );
  };
  client.on('data', triggerEvent("data"));
  client.on('error', triggerEvent("error"));//assuming you have an error event
  return {
    add:(event,fn)=>{
      listenerCounter = listenerCounter + 1;
      if(listenerCounter>1000000){
        listenerCounter=0;
      }
      listeners.push([listenerCounter,fn,event]);
      return listenerCounter;
    },
    remove:num=>{
      listeners = listeners.filter(
        listener=>{
          num !== listener[0];
        }
      )
    }
  }
}());

//convert data to object or false
const getObjectFromData = data => {
  var array = [...data];
  var dataInBuffer="";
  array.splice(0,2);
  for (var i=0;i<array.length;i++) {
    dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
  }
  console.log(dataInBuffer);
  if (dataInBuffer.startsWith('batStat')) {
    let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
    return {
      hasBattery : lastBatteryJSON.hasBattery == 'true',
      isCharging : lastBatteryJSON.isCharging == 'true',
      lastBatteryReading : parseFloat(lastBatteryJSON.batteryLife)
    };
  }
  return false;
}

//export this function
const getBatteryData = () =>  
  new Promise((resolve,reject) => {
    const removeListeners = ()=>{
      listeners.remove(okId);
      listeners.remove(errorId);
    }
    const okId = listeners.add(
      "data",
      data=>{
        const resultObject = getObjectFromData(data);
        if(resultObject){
          resolve(data);
          removeListeners();//clean up listeners
        }else{
          //not sure of on data is triggered multiple times by client.on.data
          //  if it is then at what point do we need to reject the returned promise?
        }
      }
    )
    const errorId = listeners.add(
      "error",
      error=>{
        reject(error);
        removeListeners();//clean up listeners
      }
    )
  });

  //you can call getBatteryData like so:
  //getBatteryData()
  // .then(batteryData=>console.log(batteryData))
  // .catch(error=>console.warn("an error getting battery data:",error))

#2


1  

Your module should export a function that returns a promise that returns the desired values. Also, use const and not var when possible:

您的模块应该导出一个函数,该函数返回一个承诺,该承诺返回所需的值。此外,尽可能使用const而不是var:

let resolveObj;
const haveData = new Promise((resolve) => {
  let resolved = false;
  client.on('data', (data) => {
    const array = [...data];
    array.splice(0, 2);
    for (let i = 0; i < array.length; i++) {
      dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
    }
    console.log(dataInBuffer);
    if (dataInBuffer.startsWith('batStat')) {
      const {
        hasBattery,
        isCharging,
        batteryLife,
      } = JSON.parse(dataInBuffer.split(';')[1]);
      resolveObj = {
        hasBattery: hasBattery === 'true',
        isCharging: isCharging === 'true',
        lastBatteryReading: Number(batteryLife),
      };
      if (!resolved) resolve();
      resolved = true;
    }
    dataInBuffer = '';
  });
});
const getData = () => haveData.then(() => resolveObj);
module.exports = getData;

Then consume with

然后使用

moduleFunction().then(({ hasBattery, isCharging, lastBatteryReading }) => {
  // do something with results
});

If called before resolveObj is populated, the promise will wait until the first client.on('data' to resolve. After that, the function will return a promise that resolves immediately to the current value of resolveObj (which will be properly updated on client.on('data')

如果在resolveObj被填充之前调用,该承诺将等待到第一个客户端。(“数据”来解决。之后,函数将返回一个承诺,该承诺将立即解析为resolveObj的当前值(在client.on('data')上将对其进行适当的更新)

#1


1  

As apple commented; you can export an object and mutate it every time you receive data:

苹果说;您可以导出一个对象,并在每次接收数据时进行变异:

const data = {};
client.on('data', (data) => {
  var array = [...data];
  array.splice(0, 2);
  for (var i = 0; i < array.length; i++) {
    dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
  }
  console.log(dataInBuffer);
  if (dataInBuffer.startsWith('batStat')) {
    let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
    //mutate the data object
    data.hasBattery = lastBatteryJSON.hasBattery == 'true';
    data.isCharging = lastBatteryJSON.isCharging == 'true';
    data.lastBatteryReading = parseFloat(lastBatteryJSON.batteryLife);
  }
  dataInBuffer = '';
});
//export the data object
module.exports.batteryData = data;

Or as CertainPerformance answered you can have the caller decide when to ask for the information and provide a promise.

或者根据特定的表现,你可以让打电话的人决定什么时候询问信息并做出承诺。

Here is an extended version of CertainPerformance answer that listens to error as well so a promise can be rejected and cleans up the event listeners when promise is resolved or rejected:

下面是一个扩展版本的确定性能回答,它也会侦听错误,因此当承诺被解决或拒绝时,可以拒绝承诺并清理事件侦听器:

//wrapper for client.on to add and remove event listeners
const listeners = (function(){
  var listenerCounter = -1;
  const listeners = [];
  const triggerEvent = event => data =>{
    listeners.filter(
      listener=>listener[2] === event
    ).forEach(
      listener=>listener[1](data)
    );
  };
  client.on('data', triggerEvent("data"));
  client.on('error', triggerEvent("error"));//assuming you have an error event
  return {
    add:(event,fn)=>{
      listenerCounter = listenerCounter + 1;
      if(listenerCounter>1000000){
        listenerCounter=0;
      }
      listeners.push([listenerCounter,fn,event]);
      return listenerCounter;
    },
    remove:num=>{
      listeners = listeners.filter(
        listener=>{
          num !== listener[0];
        }
      )
    }
  }
}());

//convert data to object or false
const getObjectFromData = data => {
  var array = [...data];
  var dataInBuffer="";
  array.splice(0,2);
  for (var i=0;i<array.length;i++) {
    dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
  }
  console.log(dataInBuffer);
  if (dataInBuffer.startsWith('batStat')) {
    let lastBatteryJSON = JSON.parse(dataInBuffer.split(';')[1]);
    return {
      hasBattery : lastBatteryJSON.hasBattery == 'true',
      isCharging : lastBatteryJSON.isCharging == 'true',
      lastBatteryReading : parseFloat(lastBatteryJSON.batteryLife)
    };
  }
  return false;
}

//export this function
const getBatteryData = () =>  
  new Promise((resolve,reject) => {
    const removeListeners = ()=>{
      listeners.remove(okId);
      listeners.remove(errorId);
    }
    const okId = listeners.add(
      "data",
      data=>{
        const resultObject = getObjectFromData(data);
        if(resultObject){
          resolve(data);
          removeListeners();//clean up listeners
        }else{
          //not sure of on data is triggered multiple times by client.on.data
          //  if it is then at what point do we need to reject the returned promise?
        }
      }
    )
    const errorId = listeners.add(
      "error",
      error=>{
        reject(error);
        removeListeners();//clean up listeners
      }
    )
  });

  //you can call getBatteryData like so:
  //getBatteryData()
  // .then(batteryData=>console.log(batteryData))
  // .catch(error=>console.warn("an error getting battery data:",error))

#2


1  

Your module should export a function that returns a promise that returns the desired values. Also, use const and not var when possible:

您的模块应该导出一个函数,该函数返回一个承诺,该承诺返回所需的值。此外,尽可能使用const而不是var:

let resolveObj;
const haveData = new Promise((resolve) => {
  let resolved = false;
  client.on('data', (data) => {
    const array = [...data];
    array.splice(0, 2);
    for (let i = 0; i < array.length; i++) {
      dataInBuffer = dataInBuffer + String.fromCharCode(array[i]);
    }
    console.log(dataInBuffer);
    if (dataInBuffer.startsWith('batStat')) {
      const {
        hasBattery,
        isCharging,
        batteryLife,
      } = JSON.parse(dataInBuffer.split(';')[1]);
      resolveObj = {
        hasBattery: hasBattery === 'true',
        isCharging: isCharging === 'true',
        lastBatteryReading: Number(batteryLife),
      };
      if (!resolved) resolve();
      resolved = true;
    }
    dataInBuffer = '';
  });
});
const getData = () => haveData.then(() => resolveObj);
module.exports = getData;

Then consume with

然后使用

moduleFunction().then(({ hasBattery, isCharging, lastBatteryReading }) => {
  // do something with results
});

If called before resolveObj is populated, the promise will wait until the first client.on('data' to resolve. After that, the function will return a promise that resolves immediately to the current value of resolveObj (which will be properly updated on client.on('data')

如果在resolveObj被填充之前调用,该承诺将等待到第一个客户端。(“数据”来解决。之后,函数将返回一个承诺,该承诺将立即解析为resolveObj的当前值(在client.on('data')上将对其进行适当的更新)