javascript - 长轮询 PHP 返回 2 个结果而不是一个

标签 javascript php ajax long-polling

我正在尝试创建一个类似于 Facebook 的发布系统。所以我做了一些关于 Facebook 是如何做的研究,Facebook 使用长轮询,所以我搜索了如何实现它,我实现了它。我终于完成了,我打开了 Firefox 和 Chrome 来测试它。在发布 2 或 3 个帖子后它起作用了,但随后它会重复结果。如下所示:

Duplicate results

顺便说一句,这是第一篇文章。

这是我的网络选项卡,在此过程中: It makes 3 requests instead of two

它发出 3 个请求而不是一个。

最后是我的代码:

init.js 包含我所有的 JavaScript 代码

function getNewPosts(timestamp) {
  var t;
  $.ajax({
    url: 'stream.php',
    data: 'timestamp=' + timestamp,
    dataType: 'JSON',
})
  .done(function(data) {
    clearInterval( t );
    // If there was results or no results
    // In both cases we start another AJAX request for long polling after 1 second
    if (data.message_content == 'results' || data.message_content == 'no-results') {
        t = setTimeout( function() {
            getNewPosts(data.timestamp);
        }, 1000);
        // If there was results we will append it to the post div
        if (data.message_content ==  'results') {
            // Loop through each post and output it to the screen
            $.each(data.posts, function(index, val) {
                $("<div class='post'>" + val.post_content + "<div class='post_datePosted'>"+ val.posted_date +"</div> <br>" + "</div>").prependTo('.posts');
            });
        }
    }
})
}

$(document).ready(function(){

    // Start the autosize function
    $('textarea').autosize();

    // Create an AJAX request to the server for the first time to get the posts
    $.ajax({
        async: false,
        url: 'stream.php?full_page_reload=1',
        type: 'GET',
        dataType: 'JSON',
    })
    .done(function(data) {
        // Assign the this variable to the server timestamp
        // that was given by the PHP script
        serverTimestamp = data.timestamp;
        $.each(data.posts, function(index, val) {
            $("<div class='post'>" + val.post_content + "<div class='post_datePosted'>"+ val.posted_date +"</div>" + "</div>").prependTo('.posts');
        });
    })
    .fail(function() {
        alert('There was an error!');
    })
    // When the form is submitted
    $('#post_form').on('submit', function(event) {
        $.ajax({
            url: 'ajax/post.php',
            type: 'POST',
            dataType: 'JSON',
            data: $('#post_form').serialize()
        })
        .done(function(data) {
            // Reset the form values
            $('#post_form')[0].reset();
        })
        .fail(function() {
            // When there was an error
            alert('An error occured');
        })
        // Prevent the default action
        event.preventDefault();
    });
    // Start the actual long polling when DOM is ready
    getNewPosts(serverTimestamp);
});

还有我的stream.php

<?php
header('Content-type: application/json');
// If it was a full page reload
$lastId = isset($_GET['lastId']) && !empty($_GET['lastId']) ? $_GET['lastId'] : 0;
if (isset($_GET['full_page_reload']) && $_GET['full_page_reload'] == 1) {
    $first_ajax_call = (int)$_GET['full_page_reload'];

    // Create a database connection
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
    $sql = "SELECT * FROM `posts`";
    $stmt = $pdo->prepare($sql);
    $stmt->execute();
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // Output the timestamp since its full page reload
    echo json_encode(array(
        'fullPageReload' => 'true',
        'timestamp' => time(),
        'posts' => $posts
        ));
} else if (isset($_GET['timestamp'])) {
    // The wasted time
    $time_wasted = 0;
    // Database connection
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
    $timestamp = $_GET['timestamp'];
    // Format the timestamp to SQL format
    $curr_time = date('Y-m-d H:i:s', $timestamp);
    $sql = "SELECT * FROM `posts` WHERE posted_date >= :curr_time";
    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':curr_time', $curr_time);
    $stmt->execute();
    // Fetch the results as an Associative array
    $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
    // If there wasn't any results
    if ( $stmt->rowCount() <= 0 ) {
        // Create the main loop
        while ($stmt->rowCount() <= 0) {
            // If there is still no results or new posts
            if ($stmt->rowCount() <= 0) {
                // If we waited 60 seconds and still no results
                if ($time_wasted >= 60) {
                    die(json_encode(array(
                        'message_type' => 'error',
                        'message_content' => 'no-results',
                        'timestamp' => time()
                        )));
                }
                // Helps the server a little bit
                sleep(1);
                $sql = "SELECT * FROM `posts` WHERE posted_date >= :curr_time";
                $stmt = $pdo->prepare($sql);
                $stmt->bindParam(':curr_time', $curr_time);
                $stmt->execute();
                $posts = $stmt->fetchAll(PDO::FETCH_ASSOC);
                // Increment the time_wasted variable by one
                $time_wasted += 1;
            }
        }
    }
    // If there was results then we output it.
    if ($stmt->rowCount() > 0) {
        die( json_encode( array(
            'message_content' => 'results',
            'timestamp' => time(),
            'posts' => $posts,
            )));
        exit();
    }
}

这是我的ajax/post.php:

<?php
if ( isset($_POST['post_content']) ) {
    $post_content = strip_tags(trim($_POST['post_content']));
    if ( empty($post_content) ) {

        /* If the user doesn't enter anything */
        echo json_encode(array(
            'message_type' => 'error',
            'message_content' => 'It seems like your post is empty'
            ));
    } else {
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'akar', 'raparen');
        $sql = "INSERT INTO `posts` (`post_id`, `post_content`, `posted_date`) VALUES (NULL, :post_content, NOW());";
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(':post_content', $post_content);
        $stmt->execute();
        echo json_encode(array(
            'message_type' => 'message',
            'message_content' => 'Your post has been posted successfully.'
            ));
    }
}

不懂就问我。我知道这是肮脏的代码,我重复了很多次。我这样做是为了测试,所以这并不重要。

谢谢!

最佳答案

坦率地说,我不明白您为什么要进行这种优化,除非您计划处理数以千计的消息。您也可以在每次刷新页面时获取全部内容。

每秒向服务器发送来自每个客户端的请求无论如何都会产生大量流量,因此优化应该从定义更合理的轮询周期或更智能、自适应的刷新机制开始,恕我直言。

现在,如果您真的想要这样做,则必须进行适当的同步。如果您弄乱了时间戳,您可以跳过其他客户端在触发自动刷新时添加的消息,或者两次获取上述消息。

您所有的超时处理都是不必要的。如果出现问题,通过 Ajax 的服务器请求将产生一个错误事件,这意味着连接或您的服务器出现故障,或者您的 PHP 端代码发脾气并需要修复。

对于应用程序结构的想法:

  • 更新请求会将时间戳传递给 PHP,要求检索比所述时间戳更新的所有帖子。初始值将为 1/1/1970 或其他任何值,以便初始提取检索所有现有帖子。新的时间戳将包含在响应中,以便增量请求将跳过已获取的数据。
  • Javascript 会定期生成此类请求(我宁愿将周期设置为 30 秒左右,以避免过度的服务器负载 - 假设您的普通用户可以承受等待下一批伪推文的挫败感)
  • 提交新帖子只会将其添加到数据库中,但由于所有操作都是在服务器端完成的,因此您无需担心竞争条件。

您所有的“time_wasted”和“cur_time”代码都应该放到垃圾箱中。 唯一需要的时间引用是来自该特定客户端的最后一次读取请求的日期。
所有你需要的服务器端(在你的“流”PHP文件中)是一个数据库请求来获取比客户端提供的时间戳更新的帖子,它将返回一个(可能是空的)帖子列表和相同时间戳的更新值。

坦率地说,除了这些可能令人困惑的时间戳,您还可以使用最后获取的帖子的唯一标识符(使用 0 或初始请求的任何常规值)。

关于javascript - 长轮询 PHP 返回 2 个结果而不是一个,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26233127/

相关文章:

javascript - 我如何使用 for 循环进行迭代,以在 javascript 中使用 Object.create 动态创建许多对象?

ajax - Grails从xhr获取文件

javascript - JQuery $.post 在某些 PC 上无法运行

javascript - Sharepoint 2013 中的事件单击单选按钮 JQuery

javascript - sendKeys(index) 中值的 Protractor 循环

javascript - 比较JS中的两个日期

javascript - WordPress:一页滚动 JQuery 脚本不起作用?

php - 在 Woocommerce 中编写条件自定义产品选项卡有更好的方法吗?

php - 检查mysql查询是否返回空结果集

javascript - Backbone.js 使用计时器刷新 View ?