Vivien Adnot Vivien Adnot - 3 months ago 8
JSON Question

Async fetch data, aggregate the result to an object. Is the object locked by javascript by default?

Let's say I wanna get some data from 1000 servers. Each server returns the same json structure. Dummy Example:

{
logins: 5000,
clicks: 1000
}


Let's say that I need to sum all logins from each server response.

Instead of query all the json and then perform the sum, I would like to do it in every callbacks (= 1000 times). Basic Example:

var result = 0;

$http.get(url).then(function(response) {
result += response.data.logins;
});


An example to explain why the lock is necessary:

If my first server returns 1000, the second 2000 and the third 3000;

Let's say the second hasn't finished yet when the third callback is called my the promise, for whatever reason.

If there is no lock on result, it will probably equal 4000 at the end of the third callback, which is wrong (the correct value is 6000);

So what do you think guys ? Is the result locked automatically or not ? If not, is it easy to create the lock pattern in js ?

Thanks for your answers !

Answer

I update the answer because the question was edited:

If you send many ajax calls requests, responses might not come in order, so if you need the order, you can force the ajax calls to run synchronously (if you use $http from Angular you can't).

But I would never do that (I would always use async), especially because the number of request you want to send...

If you want you can do something like just call the next endpoint on the previous success callback:

$http.get(url).then(function(response) {
    result += response.data.logins;
    $http.get(url).then(function(response) {
        result += response.data.logins;
        /// ... (use a loop)
    });
});

Example:

const data = [
{
  isActive: "1",
  logins: 1000,
  clicks: 1000
},
{
  isActive: "1",
  logins: 2000,
  clicks: 1000
},
{
  isActive: "1",
  logins: 3000,
  clicks: 1000
}];

const callApi = (index, timeout) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({data: data[index]}), timeout);
  })
};

let result = 0;
let timeouts = [0, 3000, 0];

const callback = (resp, index) => {
  result += resp.data.logins;
  console.log(`Result now in callback ${index + 1}:`, result);
	
  if (index < timeouts.length - 1) {
    index++;
    callApi(index, timeouts[index]).then(resp => callback(resp, index))
  }
		
}

callApi(0, timeouts[0]).then(resp => callback(resp, 0))

Depending on what you want to achieve I would use a key to track the request in the callback even using the server if you need it.

If you only need the sum of all 'logins' counters and you do not need the order

const data = [
{
  isActive: "1",
  logins: 1000,
  clicks: 1000
},
{
  isActive: "1",
  logins: 2000,
  clicks: 1000
},
{
  isActive: "1",
  logins: 3000,
  clicks: 1000
}];

const callApi = (index, timeout) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve({data: data[index]}), timeout);
  }) 
};

let result = 0;
[0, 3000, 0].forEach((timeout, index) => {
  callApi(index, timeout)
    .then(res => {
      result += res.data.logins
      console.log(`Result now in callback ${index + 1}:`, result)
    })
});