由JS Promises排序的AJAX调用似乎以正确的顺序运行,但数据库的输出表明不然

我在这里有两个文件。 query.php是我写的处理这个特定网站的所有MySQL查询的脚本。 query_test.js是一系列AJAX调用来测试query.php。正如你将在下面看到的,使用Promises将一个接一个的测试序列化,因为我已经使测试依赖于另一个测试。通过写入控制台,AJAX呼叫出现始终按照正确的顺序完成。问题在于输出不一致,单个测试的输出结果并不总是反映出他们面前的测试结果。由于单个测试似乎工作得很好,我坚信某些事情是排序问题。我无法弄清楚为什么会发生这种情况。由JS Promises排序的AJAX调用似乎以正确的顺序运行,但数据库的输出表明不然

为了清楚起见,测试如下:

  • 测试1:检索整个表格,显示
  • 测试2:添加新行,然后检索和显示该表再次
  • 测试3:选择刚刚添加的行,显示结果
  • 测试4:更新刚刚添加的行,检索并显示表
  • 测试5:删除刚刚添加的行,retr ieve和显示表

在下面的示例输出,注意试验2后检索的表不反映加入一排与试验3的查询返回什么。直到测试4,以前添加的行似乎显示出来。每次执行产生不同的输出;有时甚至是完全正常的,而其他时间的确像上面那样错误。控制台输出总是显示每个呼叫按其应该的顺序排列。这让我觉得后端的东西出了问题,但我对这是什么感到无能为力。

query.php

<?php 

/**

The purpose of this script is to query the database in a number of ways.

This script should be called via AJAX. See below for supported actions and

required parameters for each.

-------- ACTIONS ($_POST["action"])-----------

1. GET_TABLE: retrieve an entire table

- Parameters:

"table_name": [String] name of the table

- Returns: [JSON] the entire table

2. UPDATE_TABLE: update a row in a table

- Parameters:

"table_name": [String] name of the table

"queries": [array] a list of queries, like so: <column>[<relational_operator]<value> to find rows to update

"values": [array] key=>value pairs for each column to be updated.

3. SELECT_TABLE: select specified columns from specified rows

- Parameters:

"table_name": [String] name of the table

"queries": [array] a list of queries, like so: <column>[<relational_operator]<value>

"columns": (optional) [array] a list of column names to be returned. default value is '*', or all columns

- Returns: [JSON] the rows returned from the query

4. ADD_ROW: add a row to a table

- Parameters:

"table_name": [String] name of the table

"values": [array] key=>value pairs of columns names and corresponding values for the new row

5. REMOVE_ROW: remove a row or rows from a table

- Parameters:

"table_name": [String] name of the table

"queries": [array] a list of queries, like so: <column>[<relational_operator]<value>

**/

// Constants

// Actions

define("GET_TABLE", 100);

define("UPDATE_TABLE", 101);

define("SELECT_TABLE", 102);

define("ADD_ROW", 103);

define("REMOVE_ROW", 104);

$server = "localhost";

$username = "root";

$password = "password";

$db_name = "test";

$conn = new mysqli($server, $username, $password, $db_name);

if ($conn->connect_error) die("Connection failed: " . $conn->connect_error);

// Action must be set

checkPOST("action") or die("Error: POST variable 'action' must be setand not empty.");

$action = $_POST["action"];

// Table name must be given for all actions

checkPOST("table_name") or die("Error: POST variable 'table_name' must be set and not empty.");

$table = $_POST["table_name"];

// Generic error message

$param_err = "Error: ensure all required params are set and not empty.";

// See which action needs to be done

switch ($action) {

case GET_TABLE:

// Simply run the query

$q = "SELECT * FROM $table";

$result = $conn->query($q);

$result or die("Query '" . $q . "' failed: " . $conn->error);

if ($result->num_rows > 0) {

$output = array();

while ($row = $result->fetch_assoc()) {

$output[] = $row;

}

echo json_encode($output);

}

break;

case UPDATE_TABLE:

// Check for additional required params

(checkPOST("values") && checkPOST("queries"))

or die($param_err);

$values = $_POST["values"];

$queries = $_POST["queries"];

$q = "UPDATE $table";

// Add the values to be set to the query

$q .= " SET ";

addQuotesToStrings($values);

addItemsToQuery($q, $values, true);

// Add the WHERE clause at the end of the query

$q .= " WHERE ";

addItemsToQuery($q, $queries, false);

// Now ready to send off the query to the db and report success or failure

$conn->query($q) or die("Query '" . $q . "' failed: " . $conn->error);

echo "Successfully updated " . $conn->affected_rows . " rows.";

break;

case SELECT_TABLE:

// Check for additional required params

checkPOST("queries") or die($param_err);

$queries = $_POST["queries"];

$q = "SELECT ";

// Add columns if specified

if (checkPOST("columns")) {

$columns = $_POST["columns"];

addItemsToQuery($q, $columns, false);

}

else $q .= "* "; // No columns specified. Select all

// Add table name

$q .= "FROM $table ";

// Add queries

$q .= "WHERE ";

addItemsToQuery($q, $queries, false);

// Now, send off query

$result = $conn->query($q);

$result or die("Query '" . $q . "' failed: " . $conn->error);

if ($result->num_rows > 0) {

$output = array();

while ($row = $result->fetch_assoc()) {

$output[] = $row;

}

echo json_encode($output);

}

break;

case ADD_ROW:

// Check for POST var "values"

checkPOST("values") or die($param_err);

$values = $_POST["values"];

$q = "INSERT INTO $table";

// First, add column names

$q .= " (";

addItemsToQuery($q, array_keys($values), false);

$q .= ") ";

// Add the values

$q .= "VALUES (";

addQuotesToStrings($values);

addItemsToQuery($q, $values, false);

$q .= ")";

// Run the query

$conn->query($q) or die("Query '" . $q . "' failed: " . $conn->error);

echo "Query was successful.";

break;

case REMOVE_ROW:

// Check for queries

checkPOST("queries") or die($param_err);

$queries = $_POST["queries"];

$q = "DELETE FROM $table";

// Add queries

$q .= " WHERE ";

addItemsToQuery($q, $queries, false);

// Run query

$conn->query($q) or die("Query '" . $q . "' failed: " . $conn->error);

echo "Query affected " . $conn->affected_rows . " rows.";

break;

default:

die("Error: POST variable 'action' has an unknown value.");

}

/**

Adds items from an array to an SQL query string

Assumes a space is present before the last keyword of the existing query.

@param string &$q A reference to an SQL query string

@param array $items An array containing strings that need to be added to a query in a list format (e.g. item1,item2,item3)

$param boolean $pairs A boolean that indicated whether the items are key=>value pairs or not

**/

function addItemsToQuery(&$q, $items, $pairs) {

$first = true;

foreach ($items as $name => $item) {

if (!$first) $q .= ", ";

else $first = false;

$q .= $pairs ? $name . "=" . $item : $item;

}

}

/**

Adds single quotes to each string in a array of items for the purpose of being added to a MySQL query

@param array $values A reference to an array of items

**/

function addQuotesToStrings(&$values) {

foreach ($values as &$value) {

if (strcmp(gettype($value), "string") == 0) $value = "'" . $value . "'";

}

unset($value);

}

/**

Simple helper function to check if a POST var is set and not empty

@param string $name The name of the POST variable

**/

function checkPOST($name) {

return isset($_POST[$name]) && !empty($_POST[$name]);

}

?>


query_test.js

// This script is a series of AJAX calls to test query.php 

const GET_TABLE = 100;

const UPDATE_TABLE = 101;

const SELECT_TABLE = 102;

const ADD_ROW = 103;

const REMOVE_ROW = 104;

/**

Runs an AJAX request to query.php and displays the result

@param {jQuery} div A jQuery object div to place results

@param {array} params An assoc. array of POST variables for the AJAX call

@param {boolean} displayAsTable Determines where the result will be displayed as a table or as it is

@return {Promise} A Promise object

**/

function runTest(div, params, displayAsTable) {

return Promise.resolve($.post("../php/query.php", params, function(data) {

// Display result

if (displayAsTable) {

let tableData = JSON.parse(data);

let table = $('<table></table>');

for (let rowKey in tableData) {

let row = $('<tr></tr>');

for (let colKey in tableData[rowKey]) {

let col = $('<td></td>');

col.html(tableData[rowKey][colKey]);

row.append(col);

}

table.append(row);

}

div.append(table);

}

else div.append($('<p>' + data + '</p>'));

}));

}

var num = 0;

// Temporary function to test if Promise order holds up

function logOrder(test) {

num++;

console.log(test + " finished: " + num);

}

// ----------- Test 1: Retrieve table ----------------

let div = $('<div></div><br>');

let header = $('<h2>Test 1: Retrieve table</h2>');

div.append(header);

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

var promise = runTest(div, params, true);

// ------------ Test 2: Add Row ----------------------

promise = promise.then(function(value) {

logOrder("Test 1");

let div = $('<div></div><br>');

let header = $('<h2>Test 2: Add Row</h2>');

div.append(header);

$('body').append(div);

let values = {title: "test_proj", pic: "none.jpg", brief: "testing", description: "this is a test"};

let params = {action: ADD_ROW, values: values, table_name: "projects"};

runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure row has been added

promise = promise.then(function(value) {

logOrder("Test 2");

let div = $('<div></div><br>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 3: Select table -------------------

promise = promise.then(function(value) {

logOrder("Test 2 Check");

let div = $('<div></div><br>');

let header = $('<h2>Test 3: Select Table</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let params = {action: SELECT_TABLE, table_name: "projects", queries: queries};

runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 4: Update table -------------------

promise = promise.then(function(value) {

logOrder("Test 3");

let div = $('<div></div><br>');

let header = $('<h2>Test 4: Update Table</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let values = {brief: "This was updated", description: "This was also updated"};

let params = {action: UPDATE_TABLE, table_name: "projects", queries: queries, values: values};

runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure table has been updated

promise = promise.then(function(value) {

logOrder("Test 4");

let div = $('<div></div><br>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 5: Remove Row(s) ------------------

promise = promise.then(function(value) {

logOrder("Test 4 Check");

let div = $('<div></div><br>');

let header = $('<h2>Test 5: Remove Row(s)</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let params = {action: REMOVE_ROW, table_name: "projects", queries: queries};

runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure row has been removed

promise = promise.then(function(value) {

logOrder("Test 5");

let div = $('<div></div>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

runTest(div, params, true);

}, function(error) {

alert(error);

}).then(function(value) {

logOrder("Test 5 Check");

}, function(error) {

alert(error);

});


输出示例:

Test 1: Retrieve table 

11 placeholder none.jpg placeholder placeholder description

Test 2: Add Row

Query was successful.

11 placeholder none.jpg placeholder placeholder description

Test 3: Select Table

Test 4: Update Table

Successfully updated 1 rows.

11 placeholder none.jpg placeholder placeholder description

17 test_proj none.jpg This was updated This was also updated

Test 5: Remove Row(s)

Query affected 1 rows.

11 placeholder none.jpg placeholder placeholder description

回答:

我在看着this后发现自己做错了。

我做了@DarkKnight建议的更改,但这不是问题,或者至少它不是唯一的问题。

原来我只需要在每个promise.then()成功函数中返回一个承诺。本来,我没有这样做。因此,在第一次测试之后,每个后续测试都开始查询,但随后调用下一个then()而不是等待结果,因为新的承诺没有做任何事情。

这是新query_test.js

// This script is a series of AJAX calls to test query.php 

const GET_TABLE = 100;

const UPDATE_TABLE = 101;

const SELECT_TABLE = 102;

const ADD_ROW = 103;

const REMOVE_ROW = 104;

/**

Runs an AJAX request to query.php and displays the result

@param {jQuery} div A jQuery object div to place results

@param {array} params An assoc. array of POST variables for the AJAX call

@param {boolean} displayAsTable Determines where the result will be displayed as a table or as it is

@return {Promise} A Promise object

**/

function runTest(div, params, displayAsTable) {

return new Promise(function(resolve, reject) {

$.post("../php/query.php", params, function(data) {

// Display result

if (displayAsTable) {

let tableData = JSON.parse(data);

let table = $('<table></table>');

for (let rowKey in tableData) {

let row = $('<tr></tr>');

for (let colKey in tableData[rowKey]) {

let col = $('<td></td>');

col.html(tableData[rowKey][colKey]);

row.append(col);

}

table.append(row);

}

div.append(table);

}

else div.append($('<p>' + data + '</p>'));

}).then(resolve, reject)

});

}

var num = 0;

// Temporary function to test if Promise order holds up

function logOrder(test) {

num++;

console.log(test + " finished: " + num);

}

var promise = Promise.resolve();

// ----------- Test 1: Retrieve table ----------------

promise = promise.then(function(value) {

let div = $('<div></div><br>');

let header = $('<h2>Test 1: Retrieve table</h2>');

div.append(header);

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

return runTest(div, params, true);

}, function(error) {

alert(error);

});

// ------------ Test 2: Add Row ----------------------

promise = promise.then(function(value) {

logOrder("Test 1");

let div = $('<div></div><br>');

let header = $('<h2>Test 2: Add Row</h2>');

div.append(header);

$('body').append(div);

let values = {title: "test_proj", pic: "none.jpg", brief: "testing", description: "this is a test"};

let params = {action: ADD_ROW, values: values, table_name: "projects"};

return runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure row has been added

promise = promise.then(function(value) {

logOrder("Test 2");

let div = $('<div></div><br>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

return runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 3: Select table -------------------

promise = promise.then(function(value) {

logOrder("Test 2 Check");

let div = $('<div></div><br>');

let header = $('<h2>Test 3: Select Table</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let params = {action: SELECT_TABLE, table_name: "projects", queries: queries};

return runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 4: Update table -------------------

promise = promise.then(function(value) {

logOrder("Test 3");

let div = $('<div></div><br>');

let header = $('<h2>Test 4: Update Table</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let values = {brief: "This was updated", description: "This was also updated"};

let params = {action: UPDATE_TABLE, table_name: "projects", queries: queries, values: values};

return runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure table has been updated

promise = promise.then(function(value) {

logOrder("Test 4");

let div = $('<div></div><br>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

return runTest(div, params, true);

}, function(error) {

alert(error);

});

// -------------- Test 5: Remove Row(s) ------------------

promise = promise.then(function(value) {

logOrder("Test 4 Check");

let div = $('<div></div><br>');

let header = $('<h2>Test 5: Remove Row(s)</h2>');

div.append(header);

$('body').append(div);

let queries = ["title='test_proj'"];

let params = {action: REMOVE_ROW, table_name: "projects", queries: queries};

return runTest(div, params, false);

}, function(error) {

alert(error);

});

// Get table to ensure row has been removed

promise = promise.then(function(value) {

logOrder("Test 5");

let div = $('<div></div>');

$('body').append(div);

let params = {action: GET_TABLE, table_name: "projects"};

return runTest(div, params, true);

}, function(error) {

alert(error);

});

promise.then(function(value) {

logOrder("Test 5 Check");

}, function(error) {

alert(error);

});

回答:

runTest()Promise.resolve创建一个Promise解析为一个jQuery Deferred对象立即,所以这些AJAX调用实际上并行启动。

为了让AJAX调用在系列开始,runTest()应该返回一个Promise解析的结果,而不是由converting jQuery的递延对象承诺:

function runTest(div, params, displayAsTable) { 

return new Promise(function (resolve, reject) {

$.post("../php/query.php", params, function(data) {

// Display result

}).then(resolve, reject)

})

}

,或者你可以尝试jQuery的3.0,Deferred应与Promise工作无需转换。

以上是 由JS Promises排序的AJAX调用似乎以正确的顺序运行,但数据库的输出表明不然 的全部内容, 来源链接: utcz.com/qa/258928.html

回到顶部