这对AJAX请求是竞争条件吗?

Is this pair of AJAX requests a race condition?

本文关键字:条件 竞争 AJAX 请求 这对      更新时间:2023-09-26

我正在创建一个以新闻文章为特色的网站。这些文章将出现在页面底部的两栏中。底部将有一个按钮来加载其他新闻故事。这意味着我需要能够指定要加载的新闻故事。在服务器端,我只是在SQL语句中使用一个LIMIT子句来实现这一点,并提供:first参数,如下所示:

SELECT * 
FROM news 
ORDER BY `date` DESC 
LIMIT :first, 1

这意味着,在客户端,我需要跟踪我加载了多少新闻项。我通过将加载新信息的函数保存在具有保存加载项数量的属性的对象中来实现这一点。我担心这在某种程度上是一个我没有看到的竞争条件,但是,在这个数字增加之前,我的loadNewInformation()将被调用两次。我的代码如下:

var News = {
    newInfoItems: 0,
    loadNewInformation: function(side) {
        this.newInfoItems += 1;
        jQuery.get(
            '/api/news/'+ (this.newInfoItems - 1),
            function(html) {
                jQuery('div.col'+side).append(html);
            }
        );
    }
}

在页面加载时,以以下方式调用:

News.loadNewInformation('left');
News.loadNewInformation('right');

我可以这样实现:第一个调用的成功处理程序为第二个调用发出另一个AJAX请求,这显然不会是一个竞争条件……但这看起来像是草率的代码。想法吗?

(是的,存在竞争条件)

只寻址JavaScript

一个页面上的所有JavaScript代码(不包括Web-Workers),包括回调,都是"互斥"运行的。

在这种情况下,因为newInfoItems是急切求值的,它甚至没有那么复杂:"/api/news/0"answers"/api/news/1"都保证被获取(或者在尝试中失败)。与此比较:

// eager evaluation: value of url computed BEFORE request
// this is same as in example (with "fixed" increment order ;-)
// and is just to show point
var url = "/api/news/" + this.newInfoItems
this.newInfoItems += 1;
jQuery.get(url, 
    function(html) {
        // only evaluated on AJAX callback - order of callbacks
        // not defined, but will still be mutually exclusive.
        jQuery('div.col'+side).append(html);
    }
);
但是,AJAX请求完成的顺序没有定义,并且受到服务器和浏览器的影响。此外,如下所述,在服务器和单个AJAX请求之间没有建立原子上下文。

在上下文中寻址JavaScript

现在,即使已经确定"/api/news/0" "/api/news/1"将被调用,想象这种不太可能,但理论上是可能的情况:

  • 文章B,A存在于数据库
  • 浏览器发送两个AJAX请求——异步发送或同步发送,这无关紧要!
之间的某个时间将文章添加到数据库中
  • 服务器处理news/0请求,
  • 服务器处理news/1请求

然后,发生以下情况:

  • news/0返回文章B(文章B,A在数据库中)
  • article C新增
  • news/1返回文章B(文章C,B,A在数据库中)

注意文章B被返回两次!哦:)

因此,尽管竞态条件"似乎不太可能",但它确实存在。如果在news/0之前处理news/1,并且(再次)在
请求之间添加文章,则可能发生类似的竞争条件(结果不同):在两种情况下都没有原子保证 !

(如果随着添加新文章的时间增加而串行执行AJAX请求,则上述竞争条件更有可能是)

可能的解决方案

考虑在单个请求中获取n(2是可以的!)文章(例如:"/api/latest/n"),然后在回调

中按适当的排列文章。例如,文章的前半部分在左边,后半部分在右边,或者任何合适的地方。

除了通过使单个请求成为原子操作来消除上述特定的竞争条件(关于条目添加)之外,还将导致更少的网络流量和服务器的工作量减少。

API的取回可能看起来像:

SELECT * 
FROM news 
ORDER BY `date` DESC 
LIMIT :n

幸福的编码。

是的,从技术上讲,这可能会创建某种竞争条件。调用是异步的,如果第一个调用由于某种原因被阻塞,第二个调用可以先返回。

然而,由于在你的回调函数中没有太多依赖于另一边被填充的存在的事情,所以我不认为它应该给你带来太多的痛苦。

不应该是任何竞态条件,一定是代码中其他地方出了问题。在.get()调用之前,计数器是递增的,所以在每次get之前,计数器应该正确递增。一个简短的示例来演示顺序调用时的工作原理:http://jsfiddle.net/KkpwF/

我想你是在暗示newItemInfo计数器?

观察:

  1. 你调用News.loadNewInformation两次,与一个回调函数,将执行AJAX完成。
  2. 你提供的回调方法不会改变News对象。

在这种情况下,你不必担心。对News.loadNewInformation的第二次调用将只在第一次调用完成后执行——也就是说,不执行回调方法。因此,newItemInfo计数器将包含正确的值。

试试这个:

loadNewInformation: function(side) {
    jQuery.get(
        '/api/news/'+ (this.newInfoItems++),
        function(html) {
            jQuery('div.col'+side).append(html);
        }
    );
}