-1

I am developing a front-end application that loop through an array comprised of acceleration values and timestamps. These values will be used in animating a div (a moving vehicle).

To prevent parsing and loading the entire JSON file at once, I added a setTimeout to loop through the strings every second. To make use of this incoming data, I then need to access these values in another function which will be fed to css keyframes as a dynamic values. The ultimate goal is to animate the div according to the incoming data.

I have an issue in accessing the value from the json string outside of the myLoop() function.

I have the following json string:

data=[
    {
        "time": 0,
        "accX": 0.11,
        "accZ": 0.11
    },
    {
        "time": 86400,
        "accX": 0.11,
        "accZ": 0.11
    },
    {
        "time": 172800,
        "accX": 0.11,
        "accZ": 0.11
    },
    {
        "time": 259200,
        "accX": 0.11,
        "accZ": 0.11
    },
    {
        "time": 345600,
        "accX": 0.35,
        "accZ": 0.11
    }
]

Here is my function that loop through the json string every second:

    var i = 0; 
    var data = JSON.parse(JSON.stringify(data));
    let acceleration_x,acceleration_z,timestamp;
    
    function myLoop() {
      window.setTimeout(function() {   
        acceleration_x = data[i].accX;
        acceleration_z = data[i].accZ;
        timestamp      = data[i].time;
        i++;                    
        if (i < data.length) {          
          myLoop();  
        } 
        console.log(acceleration_x, acceleration_z,timestamp)                
      }, 1000)
      return {
        acceleration_x,
        acceleration_z,
        timestamp
    };
    }

I need to access acceleration_x, acceleration_z and timestamp in this function:

function play(animation) {
  myLoop(); // Here i need to access the acceleration and timer value and feed it to the keyframes
    $('.track').resetKeyframe(function() {
        switch (animation) {
            case 'normal':

            $('.track').playKeyframe({
                name: 'carMove',
                duration: "13s",
                timingFunction: 'linear',
                iterationCount: 'infinite',
                direction: 'normal',
                fillMode: 'forwards'
              });

              $('.car').playKeyframe({
                name: 'shake',
                duration: "3s",
                timingFunction: 'linear',
                iterationCount: 'infinite',
                direction: 'normal',
                fillMode: 'forwards'
              });
            break;
        }
        
    })
}

I am not sure where I am doing wrong. Any help would be appreciated.

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Sami
  • 341
  • 7
  • 22
  • 1
    `data` is not JSON. The only spot with actual JSON is the return value of `JSON.stringify()` in the `JSON.parse(JSON.stringify(data))` construct. – Andreas Aug 09 '21 at 12:18
  • I got what you mean. after initialising the path of my json like I stringify and parse so I can access each individual value. But this is not my issue here, printing the data using console.log(data[i].time) inside myLoop() is fine, my problem is that I need to access these values outside myLoop() function and access them in function play(animation) {...} – Sami Aug 09 '21 at 12:21
  • You're not saving any time "parsing and loading the entire JSON file at once". If you load the first as described (via a `script` element's `src` attribute), it's already been parsed and loaded; you don't need to stringify and parse. – Heretic Monkey Aug 09 '21 at 13:06

1 Answers1

0

The reason you could not get the data outside of myLoop() is that setTimeout() runs asynchronously. It means that the setTimeout() code is enqueued in the task list and JS engine returns immediately to execute the next line. The enqueued task is invoked after 1 second by the event loop. So the return statement is actually executed before the enqueued task is invoked which results in returning no informations.

In order to obtain the result from the asynchronous task, you have to create Promise and pass the result to resolve() function. I put that code into tick() function. Just call the tick() wherever you need the data to feed to the keyframe. I left the comments for you where you should put the code for feeding keyframe.

    async function myLoop (data) {
      function tick (obj) {
        return new Promise((resolve, reject) => {
          setTimeout(() => resolve({
            acceleration_x: obj.accX,
            acceleration_z: obj.accZ,
            timestamp: obj.time
          }), 1000)
        });
      }
      /* -------------------------------------
         feed the data to your keyframe here
         ------------------------------------- */
      for (let ele of data) {
        const keyframe = await tick(ele);
        console.log(keyframe);
      };
    }

    function play () {
      myLoop(data);
    }

    play();

Below is the complete code to run and test. If you have further requirement, I can update the answer for you. Just leave comments.

const data = [{
    "time": 0,
    "accX": 0.11,
    "accZ": 0.11
  },
  {
    "time": 86400,
    "accX": 0.11,
    "accZ": 0.11
  },
  {
    "time": 172800,
    "accX": 0.11,
    "accZ": 0.11
  },
  {
    "time": 259200,
    "accX": 0.11,
    "accZ": 0.11
  },
  {
    "time": 345600,
    "accX": 0.35,
    "accZ": 0.11
  }
];


async function myLoop (data) {
  function tick (obj) {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve({
        acceleration_x: obj.accX,
        acceleration_z: obj.accZ,
        timestamp: obj.time
      }), 1000)
    });
  }
  /* -------------------------------------
     feed the data to your keyframe here
     ------------------------------------- */
  for (let ele of data) {
    const keyframe = await tick(ele);
    console.log(keyframe);
  };
}

function play () {
  myLoop(data);
}

play();
waterloos
  • 410
  • 2
  • 7