javascript - 由 JS Promises 排序的 AJAX 调用似乎以正确的顺序运行,但数据库的输出表明并非如此

标签 javascript php mysql ajax promise

我在这里处理两个文件。 query.php 是我为处理这个特定网站的所有 MySQL 查询而编写的脚本。 query_test.js 是一系列 AJAX 调用来测试 query.phpquery_test.js,正如您将在下面看到的那样,使用 Promises 对一个又一个测试进行排序,因为我已经使测试相互依赖。通过写入控制台,AJAX 调用出现 以始终以正确的顺序完成。问题是输出不一致,个别测试的输出显然并不总是反射(reflect)之前测试的结果。由于个别测试似乎工作得很好,我坚信这是一个顺序问题。我只是想不通这是为什么以及如何发生的。

为清楚起见,测试如下:

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

在下面的示例输出中,请注意在测试 2 之后检索到的表并未反射(reflect)添加了行,并且测试 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() 成功函数中返回一个 promise。本来,我不是这样做的。因此,在第一次测试之后,每个后续测试都开始查询,然后调用下一个 then() 而不是等待结果,因为新的 promise 没有做任何事情。

这是新的 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);
});

关于javascript - 由 JS Promises 排序的 AJAX 调用似乎以正确的顺序运行,但数据库的输出表明并非如此,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39459396/

相关文章:

javascript - Dojo 的 dijit.Editor 在 Adob​​e AIR 应用程序中不可编辑?

javascript - ajax 请求后将数据传递给变量

php - 在 MySQL 查询中,是否无论如何都要将 "o"读取为 "0",反之亦然?

php - MySQL 注册案例

javascript - 我如何使用 jQuery 获取给定位置的最顶部元素?

javascript - 从 WebView 中获取内容在单元测试 Android 中,使用 javascript

javascript - 修改 Highcharts 中的X轴

PHP/MySQL 显示结果列表

mysql - 多字段值的 SQL 查询

数据库 |左连接