-2

JS newbie here, trying to create some simple call via jQuery's $.get() to an API and process the received data.

The problem is that - apparently - this function gets called... well, whenever. But not at the point I would expect it to be called (i.e. where it is in the code). I would be fine with

  1. doing everything inside the get(...) as long as I can use loops, counters, etc. from the parent scope (as I'm used to)
  2. getting the received data out of the get() call first and then processing it

but neither of this seems possible.

I suppose what is happening is that this is what people mean when they are talking about asynchronous JavaScript as in AJAX. Nice and fine, but how do I solve my problem?

$(document).ready(function() {
  for (var i = 0; i < 5; i++) {
    console.log(i);
    $.get("test.php", function(data) {
      console.log(i);
    });
    console.log(i);
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

gives

00 11 22 33 44 44444

but I want it to be

000 111 222 333 444

I hope that makes sense.

Bonus question: Would I be better off using PHP to get the data as described here, converting it to JS as described here and continue as planned?

Edit / Solution:

My bonus question led me to go with something like this in the end (simplified):

<script>
<?php
$data = array();

for ($row = 0; $row < 5; $row++) {
    $response = json_decode(file_get_contents("test.php?access_token=".$row));
    array_push($data,$response);
}

$js_data = json_encode($data);
echo "var data_all = ". $js_data .";\n";
?>

$(document).ready(function(){
    //do JS stuff with the data_all array
});
</script>

This has the upside of not placing the access tokens in plain sight of everyone.

If there's something wrong with my approach I'd be happy to read about that in the comments, otherwise I'll consider this question done as I found a working solution. Thanks for pointing to the duplicate of this question, it was an interesting read.

Piwi
  • 99
  • 3
  • Main question: Firing off multiple ajax in a loop, will indeed be non-sequential in return (think of a horse race). Heck, you may even find a '0' come back after the '4' ;) Bonus question: kind of depends what you are doing, where you get the data you need, and what you want with it. – IncredibleHat Feb 22 '18 at 22:41
  • The fact that the callbacks are called asynchronously isn't what is causing your problem, instead, what's causing your problem is that they aren't being called until some time after the loop has finished. – Kevin B Feb 22 '18 at 22:49

1 Answers1

0

Welcome to asynchronous programming.

You won't be able to force it to let you loop how you want. You have to play by async's rules, you can't force it the other way around.

From a fundamental level, you have to get used to running async code in an async manner. That is why it has a callback to let you know it is done.

If you want to process a number of async requests serially, what you'll want to do is queue them up in some manner, then read the next one out of the queue when the first finishes.

// just a dummy async function that waits 100ms before finishing
const asyncFunction = (value) => new Promise(resolve => setTimeout(() => resolve(value), 100));

// data to call
const queue = ['A', 'B', 'C', 'D', 'E'];

const processQueue = (queue) => {
  if (queue.length == 0) {
    return Promise.resolve();
  } else {
    console.log('running', queue[0]);
    return asyncFunction(queue.shift())
      .then(val => {
        console.log(val);
        return processQueue(queue)
      });
  }
}

processQueue(queue)
  .then(() => console.log('all done'));

Basically, you have a queue of some data (in my case, letters, but in yours, could be URLs). You take the first one, then it off, wait for it to finish, and then run the next in the queue. You then repeat this until they are all complete.

The syntax above uses Promises (which works with $.get() and friends) which is generally the more modern method, but the callback version (as in your example) is very similar:

// just a dummy async function that waits 100ms before finishing
const asyncFunction = (value, callback) => setTimeout(() => callback(value), 100);

// data to call
const queue = ['A', 'B', 'C', 'D', 'E'];

const processQueue = (queue, callback) => {
  if (queue.length == 0) {
    callback();
  } else {
    console.log('running', queue[0]);
    asyncFunction(queue.shift(), val => {
      console.log(val);
      processQueue(queue, callback)
    });
  }
}

processQueue(queue, () => console.log('all done'));
samanime
  • 25,408
  • 15
  • 90
  • 139
  • 1. No idea why this answer was downvoted. Thank you very much, now I at least have confirmation what my problem is about. This had not been obvious to me. 2. Since much of your syntax is new to me (basically started out with JS yesterday, jQuery was advertised as "easy JS" and now I'm here), I might try to avoid asynchrononous programming before giving your suggestion a go. Still, I very much appreciate the code snippets. Will accept in a day or so if there won't be any other answers. (Which seems likely given the downvotes and the duplicate.) – Piwi Feb 23 '18 at 02:02