Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a possibility to receive several AJAX responses from PHP server simultaneously?

I want to make a progress bar on my website, which tracks execution of a PHP script.

The PHP script makes a bunch of connections with Google API and stores the data it receives in the database. Sometimes the process can take a minute.

The PHP script is located in ajax/integrations-ajax.php file and launched by GET AJAX request sent, if on the website to click #link button. Below is jQuery code for the request:

$('#link').on('click', function () {
    var interval = setInterval(trackStatus, 1000);
    $.getJSON('ajax/integrations-ajax.php', {action: 'link'}).done(function (json) {            
        if (json.result == true) {
            showMessage('The account is successfully linked.', 'success');
        } else {
            showMessage('There is an error happened.', 'danger');
        }
    })
});

This #link button, also sets interval which fires trackStatus function each second:

function trackStatus() {
    $.getJSON('ajax/status-ajax.php', { 
        action: 'status'
    }).done(function (json) {
        console.log(json.status);
    });
}

As you can see, trackStatus function sends GET AJAX requests to ajax/status-ajax.php file and should show status in browser console every second.

To implement tracking ability on the server I made the PHP script in ajax/integrations-ajax.php file to store status in the database. Its code you can see below:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
    set_status_in_database(0);
    // some execution code;

    set_status_in_database(1);
    // some execution code;

    set_status_in_database(2);
    // some execution code;

    set_status_in_database(3);
    // some execution code;
    echo json_encode(['result' => true ]);
}

And created another PHP file axax/status-ajax.php which can recover the status from the database:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'status') {
    $return['result'] = get_status_from_database();
    echo json_encode($return);
}

But the requests appear not to be working simultaneously. I can't receive responses for trackStatus function until the response on completion ajax/integrations-ajax.php script isn't received.

I made a profiling record in browser, which show that: enter image description here

So, is there a possibility to execute requests simultaneously? Or to implement the tracking ability I need to rethink the whole approach?

Thanks in advance for help!


Update Thank you all for your advice! And especially to @Keith, because his solution is the easiest and works. I have put session_write_close() function in the beginning for the script and everything works:

<?php
if(!is_ajax_request()) { exit; }
$action = isset($_GET['action']) ? (string) $_GET['action'] : '';
if ($action == 'link') {
    session_write_close();
    set_status_in_database(0);
    // some execution code;

    set_status_in_database(1);
    // some execution code;

    set_status_in_database(2);
    // some execution code;

    set_status_in_database(3);
    // some execution code;
    echo json_encode(['result' => true ]);
}

Here you can see profiling record from a browser: enter image description here

like image 503
Andrew Shaban Avatar asked Jan 06 '19 22:01

Andrew Shaban


People also ask

How many AJAX requests are there?

How many concurrent AJAX (XmlHttpRequest) requests are allowed in popular browsers? Bookmark this question. Show activity on this post. In Firefox 3, the answer is 6 per domain: as soon as a 7th XmlHttpRequest (on any tab) to the same domain is fired, it is queued until one of the other 6 finish.

How does AJAX work with PHP?

AJAX allows web pages to be updated asynchronously by exchanging small amounts of data with the server behind the scenes. This means that it is possible to update parts of a web page, without reloading the whole page. Classic web pages, (which do not use AJAX) must reload the entire page if the content should change.

Which is better AJAX or PHP?

PHP creates and outputs the Content to the Client Browser as it's a Server-Side Language and that's what it was built for, so on a request your code will access database, files etc. and then output the constructed html/text to the client. Ajax just gives the User a more Desktop like feel.


2 Answers

While PHP can handle concurrent requests without issue, one area that does get serialized is the Session, basically PHP during a request will place an exclusive lock on the SESSION, for that user. IOW: While this lock is on, other requests from the same user will have to wait. This is normally not an issue, but if you have long running requests it will block other requests, like AJax requests etc.

As a default PHP will write session data at then end off the request,. But if you are sure you no longer need to write any session data, calling session_write_close will release the lock much sooner.

More info here -> http://php.net/manual/en/function.session-write-close.php

like image 79
Keith Avatar answered Nov 15 '22 04:11

Keith


Would advise trying EventSource. Here is an example.

PHP

<?php
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache'); 

function send_message($id, $message, $progress) {
  $d = array('message' => $message , 'progress' => $progress);
  echo "id: $id" . PHP_EOL;
  echo "data: " . json_encode($d) . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

for($i=0; $i<4; $i++){
  set_status_in_database($i);
  // some execution code;
  send_message($i, "set status in database " . $i + 1 . " of 3' , $i*4);
  sleep(1);
}

send_message('CLOSE', 'Process complete');
?>

JavaScript

var es;

function startTask() {
  es = new eventSource('ajax/status-ajax.php');
  es.addEventListener('message', function(e) {
    var result = JSON.parse(e.data);
    console.log(result.message);
    if(e.lastEventId == 'CLOSE') {
      console.log('Received CLOSE closing');
      es.close();
      showMessage('The account is successfully linked.', 'success');
    } else {
      $('.progress').css("width", result.progress + '%');
    }
  });
  es.addEventListener('error', function(e) {
    console.log('Error occurred', e);
    es.close();
  });
}

function stopTask() {
  es.close();
  console.log('Interrupted');
}

$('#link').on('click', function(e) {
  e.preventDefault();
  startTask($(this));
});

Reference:

  • Show Progress Report for Long-running PHP Scripts

Hope that is useful for you.

like image 24
Twisty Avatar answered Nov 15 '22 02:11

Twisty