0

I need to execute a long (10-60 sec) PHP task from Ajax. Furthermore, the calling page needs to close the connection and go to a new page, with the PHP task finishing in the background.

At first I tried this scenario:

$.ajax({
   url: '/ff.php',  
   type: 'POST'
}).success(function(response) { 
   console.log("... returned");
});
window.top.location.href = "http://yahoo.com";      

But that never executes ff.php as I thought it should. Somehow it goes to the new page before calling the Ajax... or the connection is closed due to the new page, before it executes. This, despite the precautions I thought I needed in ff.php:

ignore_user_abort(true);
ob_end_clean();
header("Connection: close");
ignore_user_abort();  
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();  
flush();             
// Do processing here 

session_write_close();

sleep(30);

require_once($_SERVER['DOCUMENT_ROOT'].'/ajax/lib/sendPush.php');

I've seen numerous posts that that top block of code is what's needed to have the PHP return control to the caller immediately but I can't get it to work in this scenario -- I suspect it's because I'm moving to a new page. That last require statement sends a push notification to my app on my iPhone, which woks just fine. I use this to know that my PHP script has been executed and it will part of my production environment when this works.

I have also tried moving the new page URL into the Ajax success block as follows, since I thought the PHP had all the code necessary to return immediately.

$.ajax({
   url: '/ff.php',  
   type: 'POST'
}).success(function(response) { 
   console.log("... returned");
   window.top.location.href = "http://yahoo.com"; 
});

But in that case, it takes the full 30 secs before going to the next page.

The desired results are: execute the PHP, receive the push right away, and the page goes to the next page quickly. But so far I either get the next page immediately, with NO execution of ff.php or, it does execute but the browser waits the full 30 secs to go to the next page.

UPDATE: As one commenter said below "basically if you redirect before the response then your ajax call will be cancelled." ... so my follow up question is this... is there no way to know when the Ajax has been received and started execution, so I can do the redirect at that time? I looked through the Ajax events and they all seemed to be related exclusively to when execution is complete. Does this imply there is no way to handle a long ajax call via jQuery? We have to use a queue?

Emaz
  • 1
  • 4
  • Where is the file that contains the first code snippet relative to ff.php on the server? – SaidbakR Aug 13 '16 at 21:06
  • sємsєм, in the same directory. – Emaz Aug 13 '16 at 21:08
  • PHP is blocking, so you can't really... unless you push it to a queue, then run that separately. – Farkie Aug 13 '16 at 21:11
  • There's no way to let PHP do it's thing and return without blocking? I've already tried implementing a queue with partial success. Not easy at all. I would much prefer to have each page instance have it's own background PHP doing it's thing, as long as the calling page can move on. – Emaz Aug 13 '16 at 21:16
  • @Emaz Is that directory the document root of your server? – SaidbakR Aug 13 '16 at 21:26

4 Answers4

1

Try something like this:

$.ajax({
   url: '/ff.php',  
   type: 'POST'
}).success(function(response) { 
   console.log("... returned");
});
$('#status').text("I'm redirecting you to a new page...");
setTimeout(function(){window.top.location.href = "http://yahoo.com";},1000);  //Put some delay between ajax and redirect
Franz Tesca
  • 255
  • 1
  • 5
  • 19
  • So just wait a second before the redirect and hope that'll do it? – Emaz Aug 13 '16 at 21:18
  • I can't see why that wouldn't work, and I might try it and cut the wait time down. But I can't believe there isn't a more rigorous way to do this. – Emaz Aug 13 '16 at 21:20
  • 1
    @Emaz If the problem is that redirect occurs before ajax is requested, by delaying the redirect you leave the time to start the ajax request before redirecting. I'm not sure it's gonna work, just give it a try – Franz Tesca Aug 13 '16 at 21:21
  • I **really** wanted this to work. Not the most elegant solution but I thought it would work. The redirect happens quickly (I made it 300ms), and I get the push notification so I can see the PHP starts to execute. But as soon as the page redirect happens, the PHP script stops execution. It never finishes. – Emaz Aug 13 '16 at 22:02
  • You could check the state of the ajax request using readystate: `var xhr = $.ajax(/*your stuff here*/); switch(xhr.readyState){case 0: /*ajax unsent*/ break; case 1: /*ajax sent*/ break; case 2: /*headers received*/ break; case 3: /*script executing*/ break; case 4: /*script completed*/ break;}` – Franz Tesca Aug 13 '16 at 22:14
  • Clever! I tried it. It suffers from the same issue as the time delay -- it does work, but as soon as the redirect occurs the PHP will not complete. There must be something wrong in the PHP but I can't see what it is. Unless there's no way to keep it from aborting due to the redirect. – Emaz Aug 13 '16 at 22:23
  • I also think the problem is with php. In fact I tried your original script and it works with one of my php file... – Franz Tesca Aug 13 '16 at 22:27
1

Redirect after success:

$.ajax({
    url: '/ff.php',  
    type: 'POST',
    success: function(response) { 
        console.log("Returned: " + response);
        window.top.location.href = "http://yahoo.com";
    }
});

Calling jQuery AJAX is async method, so either wait for success event, or use async: false.

And as quoted in this answer, first output content, than close the connection in PHP:

ignore_user_abort(true);
set_time_limit(0);
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Connection: close");
header("Content-Length: $size");
ob_end_flush();
ob_flush();
flush();
session_write_close();     
// Do processing below:
Community
  • 1
  • 1
skobaljic
  • 9,379
  • 1
  • 25
  • 51
  • When I do that, the redirect happens after the PHP is completed -- too long. – Emaz Aug 13 '16 at 21:27
  • If you redirect before posting is completed, than the post data will be corrupted. Make PHP return success, than continue task. You can echo `connection: close` header and continue, take a look at [this post](http://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response). – skobaljic Aug 13 '16 at 21:29
  • Yes I was sure that post would work. You can see those same PHP functions in my code. I'm surprised it doesn't work. Maybe because of the page redirect is my guess. – Emaz Aug 13 '16 at 21:36
  • You close the connection before echoing anything man, what did you expect? – skobaljic Aug 13 '16 at 21:40
  • Even with that code block copied and pasted right in in place of my other block... still waits 30 secs. – Emaz Aug 13 '16 at 21:56
  • Try adding `session_write_close();` after `flush();`. Maybe also add `Content-Encoding: none` header (from [this post](http://www.supermind.org/blog/1157/send-response-to-client-in-php-and-continue-processing)). – skobaljic Aug 13 '16 at 22:34
0

Basically if you redirect before the response then your ajax call will be cancelled.

You should look for alternatives for example you can call the ajax request just to schedule the long running tasks.

Even if you find a way to hack this and to make it work, It would not be a good idea, you will never know the request was successful.

So just use cron task if you running linux, or Task Scheduler for Windows Server

meda
  • 45,103
  • 14
  • 92
  • 122
0

According to documentation of ignore_user_abort you should pass true as parameter so i'll be run as long as you want. edit: i didn't notice it's already there.

Once called PHP function through AJAX, should run until end processing regardles of user abort (or close AJAX connection).

Of course you need also set set_time_limit to extend execution time of script.

jacqbus
  • 457
  • 3
  • 13
  • Yes, this works, the PHP executes as expected. But in this scenario I cannot redirect to the next page until the PHP is completed. – Emaz Aug 13 '16 at 22:04
  • You need to change only PHP scrtipt, and leave JavaScript as in first scenario (don't wait for AJAX response). You call AJAX and immediately after redirect page, and script should run on server regardles of this. – jacqbus Aug 13 '16 at 22:07
  • Ok i see in your PHP script u run ignore_user_abort twice, so try to remove second one (that wthout "true" argument) – jacqbus Aug 13 '16 at 22:11
  • If I use the first scenario and don't wait for Ajax response, the redirect happens before the PHP even starts. If I time delay the redirect, the PHP does start, but will abort as soon as the redirect happens -- long before the PHP execution is complete. – Emaz Aug 13 '16 at 22:14
  • Even with the code block noted by skobaljic above, which is from another post, it waits the full 30 secs. – Emaz Aug 13 '16 at 22:15