I know that would be a duplicate, but I can't find what I need. I try to make a real time mmorpg (refferences:travian,tribal-wars,ikariam,etc) in order to get some experience and still have some fun (my childhood dream).
User have more then one city and he can access each one using a 'select form'.
When user changes the select form, an ajax is going to DB and returns 'current city resources: wood,iron,stone' also the 'current production' for each one. All works well. When I change the select form, ajax is updating resources bar with a loop over values class. I will update my DB stocks table with the current values + productions each hour(would be a waste to make it every 10 seconds). The problem is that I want to run a script every 5-10 seconds which should update client side resources stock something like this: "document.getElemByID(wood).html = current dbStocked wood(//which has been query once with ajax) + (wood_production/3600(//seconds in a minute)*((current_minutes*60)+(current_seconds)))". All works fine, but when I change the city with select form, setInterval keep running, now having 2 values of wood and wood_prod in script logic, now on each itineration toogle between this 2 cases. Every 5 seconds the div representing wood value gets:one time last selected city calculation, one time current city calculation. So the div content is juggling each 5 second(interval time). EDIT: The setInterval is kidnaping the value of which one started and not gonna drop it away even replaced by another, so it forces initial values and toogling them with the current ones, every 5 seconds.
Here is a part of my code:
$(document).ready(
function() {
$("#setCitty").on("change", getstats);
}
);
function getstats() {
var city = $('#setCitty').val(); //the select form triggering the ajax
var identifier = $('#identifier').val(); //don t mind it
$.ajax({
type: 'post',
url: 'handle/php/cityStatsGet.php',
dataType: "json",
data: {
city: city,
identifier: identifier,
},
success: function(response) {
console.log(response.citystats);
console.log(response.production); //Added console log here...all seems ok, changing city is changing the content of response.citystats and response.production ..
clearInterval(interval);
var v = 0;
$(".values").each(function() { //i have a mess here, i will use vanilla for performanece later
$(this).html(response.citystats[v]);
v++;
});
incoming();
setInterval(incoming, 5000);
function incoming() {
var d = new Date();
var m = d.getMinutes();
var s = d.getSeconds(); //response.citystats[19] is iron stock
$('#ironInc').html(parseInt(parseInt(response.citystats[19]) + ((parseInt(response.production[2]) / 3600)) * ((+parseInt(m) * 60) + parseInt(s))));
} //i parseint all because i have a headpain trying to figure out what is wrong just because js treats pure numbers as strings
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="values" id="ironInc"></span>
So...my question is...if I can in some way restart the execution of setInterval (because is not restarting when I recall its patern function).
EDIT: also, I have in php
$stmt = $conn->prepare("SELECT * FROM city_stats WHERE user = ? AND city = ? ");
$stmt->bind_param('ss', $username, $city);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
//here are some math to get final values eg.:$income = $row['foo']+$row['bar'];
$data = array();
$data['citystats'] = array(
$population,$workers,$houses,$happiness,$popularity,$filantropy,$terror,$tax,
$foodFactor,$innFactor,$religion,$crime,$crowding,$rats,$food,$meat,$fruits,
$chease,$bread,$kiron,$kstone,$kgold,$kwood
); //23 ELEMENTs I<23
$data['production'] = array(
$goldincome,$pwood,$piron,$pstone
);
$stmt->free_result();
$stmt->close();
$conn->close();
echo json_encode($data);
setInterval(..) returns an id for the timer. Call clearInterval(id) when you change cities.
i.E.
var id = setInterval(..);
And when changing city
clearInterval(id);
This stops the periodic refresh of wood etc from the previously selected city
You could use async functions and work with promises:
function sleepPromise(ms)
{
return new Promise(resolve => setTimeout(resolve, ms));
}
async function sleep()
{
while(true)
{
// do something
await sleepPromise(2000); // Sleep desired amount of miliseconds
// break if needed
console.log('I have awakend.');
}
}
sleep();
EDIT: Here is an example of a valid PHP file structure returning JSON after doing a MySQLi query:
<?php
$con = mysqli_connect(... //Your connection info
// Your query
$result = mysqli_query($con, "SELECT name, price FROM ...");
// If there are results
if(mysqli_num_rows($result) > 0)
{
$resultArray = array();
// Go through them row by row
while($row = mysqli_fetch_array($result))
{
// Make the associative array for each row
$arr = array ('name'=>$row[0],'price'=>$row[1]);
// Add the row to a list of rows
array_push($resultArray,$arr);
}
// set headers for JSON and json_encode the result array
header('Content-type: application/json');
echo json_encode($resultArray);
}
else echo 'error'
?>
EDIT 2: Here is your javascript code written with promises. See if this works for you:
$(document).ready(function()
{
$("#setCitty").on("change", getstats);
};
// Returns promise
function goToSleep(miliseconds)
{
return new Promise(resolve => setTimeout(resolve, miliseconds));
}
// Your tasks
function incoming(response)
{
var d = new Date();
var m = d.getMinutes();
var s = d.getSeconds();
$('#ironInc').html(parseInt(parseInt(response.citystats[19])+((parseInt(response.production[2])/3600))*((+parseInt(m)*60)+parseInt(s))));
}
// function to handle events
async function handleEvents(city, response)
{
while(city == $("#setCitty option:selected").val())
{
incoming(city, response); // call function to do stuff
await goToSleep(1000*5); // 5 seconds
}
}
function getstats()
{
var city = $("#setCitty option:selected").val(); // current selected item
var identifier = $('#identifier').val();
$.ajax(
{
type: 'post',
url: 'handle/php/cityStatsGet.php',
dataType: "json",
data: {
city: city,
identifier: identifier,
},
success: function (response)
{
handleEvents(city, response);
});
}
Related
I've been at this for more than half a day trying to figure out this problem and I swear I've tried every possible thing. So here's the idea behind what I'm trying to do... Every 10 seconds Javascript performs an AJAX call to see if you have any friends online, then returns a list of users, their status etc... Instead of formatting everything from PHP, I'll be formatting it from Javascript for various reasons... So here's what happens:
Javascript
// Let's get the data from the controller
$.post('/pagething', { datastuff }, function(data){
if(data.status == 'ok'){
// Magic nonsense here that can translate the array example:
var keys = Object.keys(data.allfriends);
}
} etc...
PHP
// Let's skip other code in the controller and focus on the important stuff
$friends_information = array(
'userid' => array();
'username' => array();
'avatar' => array();
'status' => array();
);
foreach($result_from_my_friends_model as $row){
// For ease of read, i'll just associate things with $row
$friends_information["userid"][] = $row->user_id;
$friends_information["username"][] = $row->username;
$friends_information["avatar"][] = $row->avatar;
$friends_information["status"][] = $row->status;
}
$result = array('status' => 'ok', 'allfriends' => $friends_information);
return json_encode($result);
exit();
The closest I've gotten is to either get the results by say username, or userid for example through a new object or getting the entire result but unable to distinguish between keys since object[0][1] for instance would return undefined.
Thank you in advanced, this stuff is tough to understand :/
There's no need to put each column into a separate element of $friend_array, things are generally easier if you keep all the data related to a particular friend together in an object. So do:
$result = array('status' => 'ok', 'allfriends' => $result_from_my_friends_model);
echo json_encode($result);
In the Javascript, make sure you specify that the result is JSON:
$.post('/pagething', { datastuff }, function(data) {
if (data.status == 'ok') {
$.each(data.allfriends, function(i, friend) {
// do stuff with friend.userid, friend.username, friend.avator, friend.status
});
}
}, 'json');
You already have your list_of_friend in your keysvariable of js. Just iterate through it then you will get your desired result. Best of luck
This is my sse_server.php file
include_once 'server_files/init2.php'; //this file includes the connection file to the database and some functions
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$assocArray = array();
$fetch_article = $dbh->prepare("SELECT
article_author_id,
article_author_un,
article_id,
article_cover,
article_title,
article_desc
FROM articles ORDER BY article_id DESC");
$fetch_article->execute();
while ($fetch = $fetch_article->fetch(PDO::FETCH_ASSOC))
{
$article_author_id = $fetch['article_author_id'];
$article_author_u = $fetch['article_author_un'];
$article_id = $fetch['article_id'];
$article_cover = $fetch['article_cover'];
$article_title = $fetch['article_title'];
$article_desc = $fetch['article_desc'];
$randomNum = rand(0,500);
//Insert the Random Number along with the article's info | Random Number as a Value and the contents as a Key
$assocArray[
'randomNum'.'|'. //0
$article_author_id.'|'. //1
$article_author_u.'|'. //2
$article_id.'|'. //3
$article_cover.'|'. //4
$article_title.'|'. //5
$article_desc //6
] = $randomNum;
}
//sort the array
arsort($assocArray, 1);
//echo '<pre>';
//print_r($assocArray);
//while(true){
$var = '';
foreach ($assocArray as $key => $value) {
$var .= $value .' => ' . $key . '`|||`<br>';
}
echo "retry: 6000\n";
echo "data: {$var}\n\n";
ob_flush();
flush();
//}
and this is how I'm processing the data in client.php file
<div id="feeds"></div>
<script>
if(typeof(EventSource)!=="undefined") {
var eSource = new EventSource("sse_server.php");
//detect message received
eSource.addEventListener('message', function(event) {
var jsV_feeds = event.data;
var eventList = document.getElementById("feeds");
var jsV_feedsArray = jsV_feeds.split('`|||`'); //Seperator
eventList.innerHTML = jsF_ToFetch(jsV_feedsArray);
}, false);
}
else {
document.getElementById("feeds").innerHTML="Whoops! Your browser doesn't receive server-sent events.";
}
function jsF_ToFetch(jsP_array)
{
var string = ''; //an empty string
for (var i = 0; i < jsP_array.length-1; i++)
{
jsV_Feed = jsP_array[i].split('|');
jsV_randomNum = jsV_Feed[0];
jsV_article_author_id = jsV_Feed[1];
jsV_article_author_u = jsV_Feed[2];
jsV_article_id = jsV_Feed[3];
jsV_article_cover = jsV_Feed[4];
jsV_article_title = jsV_Feed[5];
jsV_article_desc = jsV_Feed[6];
string += jsV_randomNum +'<li><b>'+jsV_article_author_u+'</b><!--process the rest in a similar way--> </li>';
} // for loop ENDS here
return '<ol>' + string + '</ol>';
}
</script>
The Problem is if I use the foreach loop only, it reconnects every 6 seconds.
And if I wrap the foreach inside a while loop it keeps the connection alive but continously keeps on sending data. This eventually loads up a lot of data within seconds. Also it makes AJAX Post request very slow which is executed via another page simultaneously.
Why is that happening ?
How can I get it to keep the connection open, not send data, and not slow down the AJAX post requests.
PS: I have visited these links -
http://www.html5rocks.com/en/tutorials/eventsource/basics/
PHP Event Source keeps executing
May be I didn't understood them very well enough. If it could be boiled down to even simpler terms, kindly do it!
Thanks in advance!
You want to be using the while(true){} loop that you've commented out in sse_server.php. Your SSE script should never exit (until the socket is closed, which would happen from client-side, i.e. your JavaScript script closing it, or the browser window being closed).
The reason you have problems when using the while loop is that there is no sleep() or wait action inside the while loop. So you are sending data to the client (the same data over and over again!), at maximum rate.
Conceptually, what I'm guessing you are after is this code:
$lastID = 0;
while(true){
$fetch_article = $dbh->prepare("SELECT something FROM somewhere WHERE conditions AND articleID > ?");
$results = $fetch_article->execute($lastID);
if(has 1+ results) {
foreach($results){
echo result formatted for SSE
$lastID = ...;
}
flush();ob_flush();
}
sleep(5);
}
This is saying it will poll the DB every 5 seconds for new records. If there are no new records it does nothing - just goes back to sleep for another 5 seconds. But if there are new records it pushes them out over SSE to the client.
You can adjust the 5 second sleep to find the balance between CPU usage on the server and latency. Shorter sleep means lower latency (your clients get the new data sooner), but higher CPU on the server.
Aside: The lastID approach above is just some way of detecting what records you have seen, and have not yet seen. It is good if you have a unique ID in your table, which is AUTO_INCREMENT. But, alternatively, if DB records are inserted with a created timestamp, then the query becomes:
$now = 0;
while(true){
$stmt = prepare( "SELECT ... AND created > ?" );
$stmt->execute( $now );
$now = time();
...process results ...
sleep(5);
}
(Slightly safer is to set $now to the maximum created timestamp that was found in results, rather than to time() each time; otherwise it is possible for a record to slip through the cracks and not get sent to clients.)
I have a stored procedure, which runs ok in Management Studio.
Then I have a php code, where I call it:
$sql = "EXEC MyProc #Param1=1, #Param2 = 2, #Param3 = '3'";
$stmt = sqlsrv_query($conn, $sql);
if ($stmt === false) {
die(print_r(sqlsrv_errors(), true));
}
while($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC))
{
// and so on ...
}
it runs ok, with some set of parameters, that return 0 records (I simply get an empty array).
But when I change the params to return values, I get 500 error in JavaScript, which is calling the PHP script.
Here's how it's called in JS:
dojo.xhrPost({
url: "/mySuperPhpCode.php",
handleAs: "text",
content: { Param1: 1, Param2: 2, Param3: 3 },
load: function (response, ioArgs) {
// some actions
},
error: function(response, ioArgs) {
// some actions
}
});
I know, that 500 error can point to many troubles, but I'm out of ideas for now.
My configuration: IIS 7.5, PHP 5.3.19. Ask, if any additional details needed.
Upd 1:
The code for processing results (inside while-loop):
$rez = array();
foreach ($row as $key => $value) {
if (($key=="SomeDateFieldName1"||$key=="SomeDateFieldName2"||
$key=="SomeDateFieldName3"||$key=="SomeDateFieldName4"||
$key=="SomeDateFieldName5") && !is_null($value) ){
$d=get_object_vars($value);
$d=$d["date"];
$zn=$d;
} else {
$zn=$value;
}
$rez[$key] = $zn;
}
$t[]=$rez ;
$t - will be returned from php-script.
One more addition: this php-file contains few calls to procedures (incoming parameters determines, which one will be called). Some of them work perfectly (in both cases: where no results and when there some records returned) and being processed with this code with no problems.
Found it!
eemikula, you was right. The problem was in the result-processing section. I was trying to call get_object_vars on non-object value (in all previous cases, in the tables with the given "SomeDateFieldNameN" were DateTime objects, but in this table it was a varchar), so I added a check for it:
is_object($value)
This question already has answers here:
How to increase the execution timeout in php?
(14 answers)
Closed 8 years ago.
I have a simple javascript function like so:
$(document).ready(function(){
var request = $.ajax({
url: "read_images.php",
type: "GET",
dataType: "html"
});
request.done(function(msg) {
$("#mybox").html(msg);
document.getElementById('message').innerHTML = '';
});
request.fail(function(jqXHR, textStatus) {
alert( "Request failed: " + textStatus );
});
});
The php script it is calling loops on the contents of a folder, runs some checks, and returns a response. The script is as follows:
//Get all Images from server, store in variable
$server_images = scandir('../images/original');
//Remove first 3 elements, which are not correct
array_shift($server_images);
array_shift($server_images);
array_shift($server_images);
$j = 0;
for($i=0;$i<count($server_images) && $i<3000;$i++) {
$server_image = $server_images[$i];
//Make sure that the server image does not have a php extension
if(!preg_match('/.php/',$server_image)) {
//Select products_id and name from table where the image name is equal to server image name
$query = "SELECT `name`
FROM `images`
WHERE `name` = '$server_image'";
$mro_images = $db->query($query);
$mro_images_row = $mro_images->fetch();
$mro_image = $mro_images_row['name'];
//If no results are found
if(empty($mro_image)) {
$images[$j] = $server_image;
$j++;
}
}
}
It works if the loop is restricted to 2000 iterations but if I try to do e.g. 3000 iterations the result is:
HTTP/1.1 500 Internal Server Error 31234ms
I've tried increasing the php execution limit, but this didn't have any effect as, after contacting my host:
Unfortunately in our environment we don't have any way to increase the loadbalancer timeout beyond 30 seconds
Therefore: How can I restructure this code to avoid hitting the execution time limit?
The below code indicates the basic logic to follow. It isn't tested code and should not be taken as a drop in code example.
Use a javascript loop
Instead of making a slow process slower - write your JavaScript to ask for smaller chunks of data in a loop.
I.e. the js could use a while loop:
$(document).ready(function(){
var done = false,
offset = 0,
limit = 20;
while (!done) {
var url = "read_images.php?offset=" + offset + "&limit=" + limit;
$.ajax({
async: false,
url: url
}).done(function(response) {
if (response.processed !== limit) {
// asked to process 20, only processed <=19 - there aren't any more
done = true;
}
offset += response.processed;
$("#mybox").html("Processed total of " + offset + " records");
}).fail(function(jqXHR, textStatus) {
$("#mybox").html("Error after processing " + offset + " records. Error: " textStatus);
done = true;
});
}
});
Note that in the above example the ajax call is forced to be syncronous. Normally you don't want to do this, but in this example makes it easier to write, and possibly easier to understand.
Do a fixed amount of work per php request
The php code also needs modifying to expect and use the get arguments being passed:
$stuff = scandir('../images/original');
$offset = $_GET['offset'];
$limit = $_GET['limit'];
$server_images = array_slice($stuff, $offset, $limit);
foreach($server_images as $server_image) {
...
}
...
$response = array(
'processed' => count($server_images),
'message' => 'All is right with the world'
);
header('Content-Type: application/json');
echo json_encode($response);
die;
In this way the amount of work a given php request needs to process is fixed, as the overall amount of data to process grows (assuming the number of files in the directory doesn't grow to impractical numbers).
If everything works with 2000 iterations for 3000 iterations try upping the time limit to allow php to execute longer. But under normal circumstances this is not a good idea. Make sure you know what you are doing and have a good reason for increasing the execution time.
set_time_limit ( 60 );
http://www.php.net/manual/en/function.set-time-limit.php
Also this could be due to the script exhausting the amount of memory. Create a file with the phpinfo function in it and then check the value for the memory_limit.
<?php phpinfo(); ?>
Then you can increase the limit by htaccess file. But again make sure you want the script to consume more memory. Be careful.
ini_set('memory_limit', '128M'); #change 128 to suit your needs
Your count($server_images) is probably resulting in an infinite loop.
If count() returns 0, your for loop will never end. So you need to check that first.
//Get all Images from server, store in variable
$server_images = scandir('../images/original');
//Remove first 3 elements, which are not correct
array_shift($server_images);
array_shift($server_images);
array_shift($server_images);
$j = 0;
if(count($server_images) > 0){
for($i=0;$i<count($server_images) && $i<3000;$i++) {
//Do something
}
}
I have been searching similar problems for hours but with no avail.
I am using Highcharts to update a graph every 3 seconds with the last entry of a specific MySQL table. I am using the example Javascript code as a guide. Here is the snippet of code I am concerned with
var chart;
$('#container').highcharts({
chart: {
type: 'spline',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function() {
// set up the updating of the chart every 3 seconds
var series = this.series[0];
setInterval(function() {
var x = (new Date()).getTime(), // current time - adjust to align with data vals
y = getyval();
series.addPoint([x, y], true, true);
}, 3000);
...where the function getyval() uses $.get():
function getyval(){
$.get('testget.php', function(output){
alert(parseFloat(output));
});
};
My testget.php file:
<?php
session_start();
$db = $_SESSION['monitorId'];
$table = $_SESSION['tableId'];
$split_table = explode("_", $table);
$param = $split_table[1];
$dbcon = mysqli_connect("localhost","root","",$db);
$query = "SELECT * FROM ".$table." ORDER BY datetime DESC LIMIT 1";
$lastentry = mysqli_query($dbcon, $query) or die('error reading table');
$row = mysqli_fetch_array($lastentry, MYSQLI_ASSOC);
$yval = $row[$param];
echo $yval;
?>
This works all well and good at "alerting" the last value every 3 seconds but when I try to assign the variable y to this result, it does not work. For example, if I change the getyval() function to:
function getyval(){
$.get('testget.php', function(output){
return parseFloat(output);
});
};
Thanks in advance!
If you use just return, it'll return from anonymous function, not from getyval().
Second problem is that getyval() is asynchronous (you'll not receive value just after calling).
You have to call that function earlier, save its result somewhere and then use it.
var yValue = null;
function getyval(){
$.get('testget.php', function(output){
yValue = parseFloat(output);
});
};
AJAX methods like $.get are asynchronous, meaning the script doesn't wait for them to finish. The function you passed to $.get can return something, but it kind of gets dumped into the bit bucket - there's nowhere useful to return it to.
Your code needs a little bit of a redesign to take full advantage of being asynchronous. Change the function inside setInterval to this:
setInterval(function() {
// current time - adjust to align with data vals
var x = (new Date()).getTime();
// we need to do this in here so `function(result)` can use `x`
$.get('testget.php', function(result) {
// this gets run when the result is received
var y = parseFloat(result);
series.addPoint([x, y], true, true);
});
}, 3000);
The work is now done when $.get gets its result and invokes its function, which is whenever your PHP runs.
(You might be worried that the async stuff will mean data points might be received out-of-order. That won't happen - you're specifying the x-coordinate manually, and each time the outer function is run a new x is created. Every y will be paired with its matching x.)