0

I want learn about jQuery AJAX using callbacks. So I create this as example.

<?php 
  sleep($_POST['sleep']);
  echo 'sleep for '.$_POST['sleep'] .'sec';
?>
$(function () {
  var timer = [5,2,3,1,4];

  function sleep(timer) {
    $.ajax({
      url: 'ajax.php',
      data: { sleep: timer },
      type: 'post',
      success: function(d){
        console.log(d);
      },
      error: function(d){
        console.log(d);
      }
    });
  } 

  $('#ajax').click(function(){
    $.each(timer, function(i, e) {
      sleep(e);
    })
  });
});

Result: 1,2,3,4,5 What I want is : 5,2,3,1,4 (synchronous)

I know I can using async: false; but this will hang up my browser and this is deprecated too. So I want to learn to doing sync using callback

Here is my fail script:

<script src="http://code.jquery.com/jquery-1.12.4.js" type="text/javascript"></script>
<script lang="javascript" type="text/javascript">
$(function () {
  var timer = [5,2,3,1,4];

  $.each(timer, function(i, e) {
    sleep(e, function(d) {
      console.log(d);
    });
  });

  function sleep(sleepTimer, callback) {
    $.ajax({
      url: 'ajax.php',
      data: { sleep: sleepTimer },
      type: 'post',
      success: callback,
      error: function(data){
        console.log(data);
      }
    });
  } 
});
</script>

Result: 1,2,3,4,5 instead 5,2,3,1,4

What am I doing wrong?

UPDATE: Thanks to people who remind me how bad is this. But i dont have any choice right now. My friends need my help to edit his drupal page. He only have admin previledge (not sysadmin). i only can edit view but cannot edit the controllers. why?

  1. only headquarters IT staff who allowed to do that.
  2. need bureaucracy and administration (user requirement, meeting, Quality Assurance [security testing + stress test). and the funny thing is, they check all the apps, not just the page which will affected by my script change only
  3. need time. 1 month is the fastest for non security change

hei.. this sounds funny right? even you just add 1 space, we need number 2 requirement that was they (headquarters staff) said. this true story. i know this because i'm in the same ministry with him.

the script is just download single file by click (of hundred), my friend wants me to add checkbox to allow his team to batch download. i can add checkbox, and download them by sequence (this is what i trying to do with synchronous ajax). but since this will hang up their browser, so i need the right way to doing this.

i have 2 choice here: using phantomjs (medium) or synchronous ajax (easy)

  • 1st reason: since he will not pay me, ofc i choose the easy path.
  • 2nd reason: i always avoid to use sync ajax as possible, but now i want to know how to doing sync ajax using callbacks.
plonknimbuzz
  • 2,594
  • 2
  • 19
  • 31
  • 4
    I understand that you want to learn, but to be clear - *synchronous AJAX is a bad practice which should be avoided in all cases unless there is absolutely no alternative* – Rory McCrossan Nov 29 '17 at 14:53
  • 1
    I can't imagine that there is a single defensible scenario for synchronous Ajax requests in the browser. – Tomalak Nov 29 '17 at 15:03
  • As an aside - your title is completely misleading: The success callback works properly. What you want to know is how to make AJAX calls _in sequence_. – random_user_name Nov 29 '17 at 15:08
  • 1
    @Tomalak the only one I've encountered is when making an AJAX request in `onbeforeunload` – Rory McCrossan Nov 29 '17 at 15:09
  • @Tomalak What if a subsequent request depends on data retrieved from a previous request? – phuzi Nov 29 '17 at 15:10
  • @phuzi That's what callbacks are for. – Tomalak Nov 29 '17 at 15:10
  • @phuzi then call the next function from within the async callback of the first one – Rory McCrossan Nov 29 '17 at 15:10
  • @RoryMcCrossan See my answer below – phuzi Nov 29 '17 at 15:11
  • @RoryMcCrossan Agreed. And even in this edge case it's only needed if you want to cancel the "unload" event based on something the server says. – Tomalak Nov 29 '17 at 15:12
  • RoryMcCrossan: thanks for your reminder, i appreciate that. @Tomalak: read my update – plonknimbuzz Nov 29 '17 at 15:48
  • @plonknimbuzz I have no idea how what you write conflicts with the solutions in the duplicates. You do not need to update any controllers. And very probably you don't need to download the files sequentially—or somehow I'm missing why the file #2 would only ready for download after file #1 has finished. As far as I understand it, all you really care for is that after everything is done, things are exactly in the same order as in your `timer` array, and the linked duplicates teach you how to do that. – Tomalak Nov 29 '17 at 15:56
  • if im not wait 1st file to finished before 2nd file finish downloaded. This will cause mass download because ajax doing async. ten files is fine, but hundred file will thrown error "file not found" because not enough bandwith to download all of them and the target server is not allow us to resume downwloading the file. like you download 200 google drive file in IDM with setting download 200 files in the same times at scheduller settings. And about the duplicate,i was write like "i know this highly possible duplicate, but since i cant do it,i need to post this question" before some1 edit mypost – plonknimbuzz Nov 29 '17 at 16:03
  • 1
    There are ways to deal with this. Some Promise libraries implement configurable concurrency, Bluebird is one of them. But it would also be possible to implement that yourself. If you can download 10 files in parallel and you have hundreds of files to download, it's a huge waste of time to download all files sequentially. – Tomalak Nov 29 '17 at 16:18
  • `it's a huge waste of time to download all files sequentially` omg why i'm not think of this XD thanks man. and for the bluebird too – plonknimbuzz Nov 29 '17 at 16:21

1 Answers1

1

TL;DR: Because it's asynchronous not synchronous

The behaviour you are seeing is completely correct for normal asynchronous calls, the order of results is not guaranteed and could be in any order. This is the best way to fire off several AJAX requests, assuming there is no dependency between calls.

For some reason you want to do this synchronously though.

You'll need to change your code so that you only send one request at a time, waiting for the first request to complete before sending the second, etc.

You can achieve this by recursively calling sleep, removing (and using) the first value from timers.

$(function () {
    var timer = [5,2,3,1,4];

    function sleep(timers){

        // check that timers is not empty first.
        if (timers == null || timers.length == 0)
            // if it is, we're done
            return;


        // remove the first value from timers
        var timer = timers.shift();

        $.ajax({
            url: 'ajax.php',
            data: {
                sleep: time
            },
            type: 'post',
            complete: function (jqXHR, textStatus) {
                // complete will get called on either success or error
                console.log(jqXHR);
                //recursively call sleep, first time through timers will be [2,3,1,4]
                sleep(timers);
            }
        });
    }

    $('#ajax').click(function(){
        // send the array of values to sleep instead
        sleep(timer);
    });
});
phuzi
  • 12,078
  • 3
  • 26
  • 50
  • 1
    This is how sequential calls would be done in jQuery Ajax. https://stackoverflow.com/questions/24931115/jquery-execute-array-of-functions-sequentially-both-deferreds-and-non-deferred – Tomalak Nov 29 '17 at 15:26
  • 1
    If OP is "learning", then it's worth mentioning the notion of [callback hell](https://stackoverflow.com/questions/25098066/what-is-callback-hell-and-how-and-why-rx-solves-it), and a better pattern probably includes using [promises](https://www.sitepoint.com/introduction-jquery-deferred-objects/) or [observables](https://medium.com/@benlesh/learning-observable-by-building-observable-d5da57405d87). – random_user_name Nov 29 '17 at 15:28
  • this answer sounds goods, @cale_b : thanks for your info. i just thinking about using promise now. ill try to find which is the best way – plonknimbuzz Nov 29 '17 at 15:42
  • i choose sequential ajax right now. easy and suits for my case. thanks all – plonknimbuzz Nov 29 '17 at 15:56