-2

I have a Php script that counts how many times a file is downloaded using Php and JQuery. I have a function getStatus() that calls a file Php every second and consumes a lot of bandwith. I tried then to call getStatus() only when the user clicks on download link, but even if I get no error, is not working as wanted, the counter (javascript part) is not updated in real time. Only putting alert() after getStatus() makes it working.

index.php

<?php include($_SERVER["DOCUMENT_ROOT"] . "/php/static_download.php"); ?>
<!DOCTYPE html>

<html lang="en">

<head>
<meta charset="UTF-8">
<title>Download Page</title>
<script src="jsc/jquery.min.js"></script>
<script src="jsc/download.js"></script>
<link rel="stylesheet" type="text/css" href="index.css">
</head>

<body>

<div class="mainbox">
  <div class="box1">
    <span class="title">Download</span>
  </div>
  <div class="cleardiv"></div>
  <div class="sbar"></div>
  <div class="cleardiv"></div>
  <div class="box2">
    <span class="row">File: <a href="files/exampleA.zip" class="link">exampleA.zip</a> downloaded <span id="item1"><?php echo $item['item1']; ?></span> times</span><br />
    <span class="row">File: <a href="files/exampleB.zip" class="link">exampleB.zip</a> downloaded <span id="item2"><?php echo $item['item2']; ?></span> times</span><br />
    <span class="row">File: <a href="test/test.zip" class="link">test.zip</a> this file will not be counted</span><br />
  </div>
  <div class="cleardiv"></div>
</div>
</body>
</html>

call_download.php

<?php    
  $item['item1'] = get_download_count('exampleA.zip');
  $item['item2'] = get_download_count('exampleB.zip');
  if (!headers_sent()) {
    header('Content-type: application/json; charset=utf-8');
  }
  echo json_encode($item);
?>

download.js

$(function() {
  getStatus();
});
function getStatus() {
  $.getJSON('/php/call_download.php', function(data) {
    $('#item1').html(data.item1);
    $('#item2').html(data.item2);
  });
  setTimeout("getStatus()",1000);
}

As you can see getStatus() function is called every one second and so I tried to modify download.js as follow:

download.js

$(document).ready(function() {
  $(document).on('click','.link',function(event) {
    getStatus();
    alert("Thanks for downloading my files!");
  });
});
function getStatus() {
  $.getJSON('/php/call_download.php', function(data) {
    $('#item1').html(data.item1);
    $('#item2').html(data.item2);
  });
}

For some reason that I cannot understand, appending alert() after getStatus() makes the counter work... if I comment alert() stops working.

Php Example

PS: I tested getStatus() and with my surprise I noted a strange thing...

$(document).ready(function() {
  $(document).on('click','.link',function(event) {
    getStatus();
    alert("Thanks for downloading my files!");
  });
});
function getStatus() {
  $.getJSON('/php/call_download.php', function(data) {
    $('#item1').html(data.item1);
    $('#item2').html(data.item2);
    alert("success");
  }).done(function(getStatus) {
    alert("success");
  }).fail(function(getStatus) {
    alert("error");
  }).always(function(getStatus) {
    alert("complete");
  });
}

with alert("Thanks for downloading my files!"); returns success, commenting //alert("Thanks for downloading my files!"); returns error... works only with alert().... the counter is not updated in real time for me...

Considerations

As you can see getStatus() function is now called only when user clicks on link thanks to setTimeout() function added inside on click event. Now works as expected.

download.js

$(document).ready(function() {
  $.ajaxSetup({cache: false});
  // $.ajaxSetup({async: false});
  $(document).on('click','.link',function(event) {
    setTimeout("getStatus()",1000);
  });
});
function getStatus() {
  $.getJSON('/test/php/call_download.php', function(data) {
    $('#item1').html(data.item1);
    $('#item2').html(data.item2);
  });
}

Thanks for the time spent to help me!

Alessandro
  • 900
  • 12
  • 23
  • 4
    If it only works when you place an alert in there, you've probalby misunderstood asynchronicity ! – adeneo Apr 11 '15 at 13:57
  • It works for me. Checked with debugger. Check your network tab, the ajax requests are sent. – Shaunak D Apr 11 '15 at 13:58
  • Both examples work fine. – Turnip Apr 11 '15 at 13:58
  • What do you mean? Maybe some delay will solve? – Alessandro Apr 11 '15 at 13:59
  • why you want separate function get status. – tech-gayan Apr 11 '15 at 13:59
  • I'm using firefox 37... and also Opera gives me the same issue, the counter is not updated... – Alessandro Apr 11 '15 at 14:00
  • I see no difference in behavior between these two demos, other than one has alerts and one doesn't. Define "not working". What is actually failing? – David Apr 11 '15 at 14:00
  • the counter downloaded xxx times is not updated... getStaus() takes the values from php and then sends them to jquery call that should update the counter in real time... – Alessandro Apr 11 '15 at 14:04
  • @Alessandro: In my browser (Safari) neither demo updates the counter without refreshing the page, but both update the counter with refreshing the page. The behavior is identical for me. – David Apr 11 '15 at 14:05
  • What does call_download.php do? Based on the code you have supplied using {async: false} is a bad solution (will unnecessarily lock the browser while running and is deprecated for that reason). – Samuel Lindblom Apr 11 '15 at 14:52
  • call_download.php reads the flat counters in the server and then prepares an array php to be sent to ajax call, then jquery sends to php target page the results that are read and displayed `` – Alessandro Apr 11 '15 at 15:16
  • How (when/by which script) is the download counter increased? – Samuel Lindblom Apr 11 '15 at 15:29
  • read my answer: http://stackoverflow.com/questions/23329531/php-making-a-download-counter-without-leaving-the-current-page/29105023#29105023 – Alessandro Apr 11 '15 at 15:39
  • @SamuelLindblom: https://api.jquery.com/category/deprecated/ `$.ajaxSetup({cache: false}); $.ajaxSetup({async: false});` are not deprecated, simply not suggested... – Alessandro Apr 11 '15 at 15:50
  • Yes, I mistyped. The {async: false} property is not deprecated, but synchronous ajax requests are deprecated in latest versions of both Chrome and Firefox since they generally suck from a UX perspective, so you shouldn't use them. – Samuel Lindblom Apr 11 '15 at 16:03
  • @SamuelLindblom: I'm using latest version of Opera (that use the same engine of Chrome) and FireFox. I'm using FireFox 37.01 and my code is still working fine... if sync is deprecated how to solve million of issue generated by default setups of jquery? – Alessandro Apr 11 '15 at 16:16
  • @SamuelLindblom: if you have another idea I'm well happy to test it... – Alessandro Apr 11 '15 at 16:17
  • "Deprecated" doesn't mean that it is removed, it means "Stop using this, it will be removed". For Firefox: "Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), synchronous requests on the main thread have been deprecated due to the negative effects to the user experience.". In Chrome you get: "Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.". – Samuel Lindblom Apr 11 '15 at 16:36
  • @Alessandro: I have added an answer with two suggestions, but I needed to know how the count is incremented before I could answer. – Samuel Lindblom Apr 11 '15 at 16:36
  • @SamuelLindblom: to know how the counter is incremented read my answer in another thread here: http://stackoverflow.com/questions/23329531/php-making-a-download-counter-without-leaving-the-current-page/29105023#29105023 – Alessandro Apr 13 '15 at 08:35

4 Answers4

1

why this hapening is that you've probalby misunderstood asynchronicity in Js as adeneo mention. so please study that area. read this Async

You cannot make a truly custom asynchronous functionin Js. You'll eventually have to leverage on a technology provided natively in Js, such as:

  • setInterval
  • setTimeout
  • etc..etc..

For a quick solution what i can give you is this.

 $(document).ready(function() {
   $(document).on('click','.link',function(event) {
$.getJSON('/test/php/call_download.php', function(data) {
$('#item1').html(data.item1);
$('#item2').html(data.item2);
alert("success");
 }).done(function(getStatus) {
alert("success");
 }).fail(function(getStatus) {
alert("error");
 }).always(function(getStatus) {
alert("complete");
  });
 });
});
Community
  • 1
  • 1
tech-gayan
  • 1,373
  • 1
  • 10
  • 25
  • This is not a good answer. The problem has nothing to do with asynchronicity and you can definitely "make a truly custom asynchronous functionin Js" - $.getJSON() is truly asynchronous, I don't know what "truly custom" means. – Samuel Lindblom Apr 11 '15 at 16:40
1

Based on my follow-up questions in the comments and your link to the call_download.php definition, your problem is that you are getting the download count before it has actually increased.

Making the request synchronous (async: false) will not have any effect (other than worsening the user experience), that would only make the succeeding code wait for $.getJSON() to finish, but you have no succeeding code.

There is no easy and reliable way for call_download.php to know if the other request (to increment the counter) was finished or not, other than dealing with timestamps.

The easiest options are:

  1. Assume that it will always take a certain amount of time for the count in the database to be incremented: Delay $.getJSON() with setTimeout() for as much time as it (usually) takes to write the count to the database, with some margin.

  2. Assume that call_download.php will always read the data from the database before the count in the database is incremented: Increment the result from $.getJSON() with $('#item1').html(data.item1 + 1);.

Community
  • 1
  • 1
Samuel Lindblom
  • 812
  • 1
  • 6
  • 22
  • No, the number or duration of the requests will not increase just because you're delaying them (which is handled by the browser, not the server). The server would not notice. If you had ``async: false`` then the page would be locked until after the server has responded, but that would also be an issue for the browser. – Samuel Lindblom Apr 11 '15 at 18:48
0

it may happened because the link event it triggered and it needs to be hold first

try:

 $(document).on('click','.link',function(event, gotoUrl) {
    if(!gotoUrl){
       event.preventDefault();
       getStatus(); // <-- maybe you should have a callback function to make sure you get response and only then trigger the click event

       $(event.target).click(event, true);
       return false;
    }

 });
Roey Zada
  • 663
  • 10
  • 30
0

Based on answer of Iceburg and Samuel Lindblom I finally wrote a function that uses setTimeout only when user click on file to download and that not uses anymore $.ajaxSetup({async: false}); that is deprecated in future versions. So it's possible to save a lot of bandwith! It's all! Thanks!

$(document).ready(function() {
  $.ajaxSetup({cache: false});
  // $.ajaxSetup({async: false});
  $(document).on('click','.link',function(event) {
    setTimeout("getStatus()",1000);
  });
});
function getStatus() {
  $.getJSON('/php/call_download.php', function(data) {
    $('#item1').html(data.item1);
    $('#item2').html(data.item2);
  });
}
Alessandro
  • 900
  • 12
  • 23