Javascript 这对AJAX请求是竞争条件吗?
我正在创建一个以新闻文章为特色的网站。这些文章将出现在页面底部的两列中。底部将有一个按钮,用于加载其他新闻故事。这意味着我需要能够指定要加载的新闻故事。在服务器端,我只是在SQL语句中使用一个LIMIT子句来实现它,并提供:first参数,如下所示:Javascript 这对AJAX请求是竞争条件吗?,javascript,jquery,ajax,race-condition,Javascript,Jquery,Ajax,Race Condition,我正在创建一个以新闻文章为特色的网站。这些文章将出现在页面底部的两列中。底部将有一个按钮,用于加载其他新闻故事。这意味着我需要能够指定要加载的新闻故事。在服务器端,我只是在SQL语句中使用一个LIMIT子句来实现它,并提供:first参数,如下所示: SELECT * FROM news ORDER BY `date` DESC LIMIT :first, 1 这意味着,在客户端,我需要跟踪我加载了多少条新闻。我通过将加载新信息的函数保存在一个对象中实现了这一点,该对象的属性包含加载的项
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”和“/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请求——异步或同步,这无关紧要李>
- 在以下时间之间的某个时间向数据库添加一篇文章:
- 服务器处理
请求,并新闻/0
- 服务器处理
请求news/1
- 服务器处理
返回文章news/0
(数据库中的文章B
)B,A
- 添加了第条
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之前,计数器应该正确递增。用一个简短的例子来说明它在按顺序调用时是有效的:我想你是在newItemInfo
计数器上暗示的
意见:
News.loadNewInformation
,回调函数将在AJAX完成时执行News
对象News.loadNewInformation
的第二次调用仅在第一次调用完成后执行,也就是说,不包括执行回调方法。因此,您的newItemInfo
计数器将包含正确的值。尝试以下操作:
loadNewInformation: function(side) {
jQuery.get(
'/api/news/'+ (this.newInfoItems++),
function(html) {
jQuery('div.col'+side).append(html);
}
);
}
有没有更优雅的方式来处理这件事?@Ryan-你能详细说明一下“优雅”吗?你的意思是“有没有一种方法可以确保它们总是按照我调用它们的顺序填充”?您已经暗示,您看到了如何通过在第一个函数的回调函数中调用第二个load来实现这一点。理想的情况是,你不想把它们纠缠在一起。您可以创建一个挂起请求队列,启动一个,然后绑定到
ajaxStop
,等待第一个请求返回,然后再启动第二个请求。我想,更“优雅”的意思是,如果高级开发人员看到,他们不会嘲笑我的东西如果一个页面出现在另一个页面之前,我并不担心,因为页面看起来仍然很好。它们被塞进绝对定位的div中,因此它们的布局不受其他div的影响。我用完全未经测试的代码创建了一个div,它可能会做一些更像您想要的事情。让我