Search code examples
javascriptphpajaxserver-sent-events

Why cannot use both ajax and server sent event?


Ajax: every time there is a click, an ajax request is sent, and the console will display the text sent by server.

"server sent event": it sends text to console in random time

if I use only ajax, I received data from server. But if I use both approaches, I did not receive data from server in response to the ajax request, but still receive message in response to "server sent event". I did not even receive a fail message from ajax request. Can someone explain this?

Client

var evtSource = new EventSource("config/addRemoveEvent.php");
evtSource.onmessage = function(e) {
  //var newElement = document.createElement("li");
  console.log("listening: "+ e.data);
  //newElement.innerHTML = "message: " + e.data;
  //eventList.appendChild(newElement);
}   

var data = {
    'action':'Initiate brief calendar', 
    'selectedMonth': month, 
    'selectedYear': year
};

var $request = $.ajax({
    type: "GET",
    dataType: "text",
    url: "config/addRemoveEvent.php",
    data: data,
    beforeSend: function(){
        console.log('start sending request');

    }
});

$request.fail(function(){
    console.log('fail');
}); 
$request.done(function(data){
    // change bg color of the cells that contain events;
    console.log(data);
});

Server

header("Content-Type: text/event-stream\n\n");
date_default_timezone_set("America/New_York");

while (1) {

  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 3);
  }

  ob_flush();
  flush();
  sleep($counter);
}

// return the date has event in a given month 
if($_GET['action'] == "Initiate brief calendar"){
    echo 'hello';
}

Solution

  • Your php script is correct for SSE:

    • it sends Content-Type: text/event-stream
    • it is an infinite loop, to keep sending data to the client

    On the other hand, when you use $.ajax it expects a server script that will send it a bunch of data and close the connection.

    Using a PHP script to do both things is possible, though has a bad code smell: two scripts, two URLs, would be better.

    Your code is almost there, actually, you just have to put the special action handling first, and then exit.

    date_default_timezone_set("America/New_York");
    
    // return the date has event in a given month 
    if($_GET['action'] == "Initiate brief calendar"){
        echo 'hello';
        exit;
    }
    
    //session_write_close();  //See below
    
    header("Content-Type: text/event-stream\n\n");
    while (1) {
    
      if (!$counter) {
        echo 'data: This is a message at time ' . $curDate . "\n\n";
        $counter = rand(1, 3);
      }
    
      ob_flush();
      flush();
      sleep($counter);
    }
    

    BTW, be careful of PHP session handling, as PHP will lock the session for the lifetime of the script. As you SSE script runs forever, that would stop any other PHP scripts running.

    So if you use sessions, but also plan to make ajax calls at the same time as an SSE script is running, have your SSE script disconnect from its session. I've shown, commented out, above where you could put a session_write_close() to achieve this.