有什么理由要使用同步XMLHttpRequest吗?


88

似乎大多数人都使用XMLHttpRequest进行异步请求,但是很明显,可以执行同步请求的事实表明这样做可能是有正当理由的。那么,那个合理的原因可能是什么呢?


1
这是一个很好的问题!我认为答案不会很有趣,但仍然是一个很好的问题。您是否尝试过同步呼叫以查看会发生什么?
塔德·多纳赫2010年

3
同步调用会阻塞浏览器,从而导致糟糕的用户体验。因此,我的问题。我想不出使用它的任何充分理由。
Darrell Brogdon

2
半严肃的答案:也许只是在您正在阅读的任何JavaScript书中填充异步请求之前的章节。
本·詹姆斯

就个人而言,我想使用一些同步ajax将多个调用包装到服务器端函数,而不必为每个调用编写请求。
Paul Ishak

在本地运行代码,尤其是内部开发框架和工具。
user2800679

Answers:


25

我认为随着HTML 5标准的发展,它们可能会变得越来越流行。如果将Web应用程序授予Web工作人员访问权限,我可以预见,开发人员将使用专用的Web工作人员发出同步请求,如乔纳森所说,以确保一个请求先于另一个请求发生。在当前只有一个线程的情况下,这是一种不太理想的设计,因为它会阻塞直到请求完成。


16
我认为他的意思是“网络工作者”
evilpie 2010年

我认为是蒸馏点。在“ UI线程”中,创建线程或各种异步任务。如果已经在线程中,则在大多数情况下使用同步。线程更加“块友好”
HaMMeReD 2011年

这是一个旧的答案,但是即使在HTML5之前,此注释也适用于甚至在HTML5之前的浏览器中提供了2个线程来发出请求。我编写了一个连接池脚本以同时使用这两个脚本,因为即使使用一个连接的异步请求,您也可以将浏览器绑定在一起。两者的结合使更多的数据可以通过行被推/拉。但是iirc,我认为同步请求仍然会限制浏览器。
vol7ron 2013年

Firefox(可能还有所有非IE浏览器)不支持异步XHR超时。HTML5 WebWorkers确实支持超时。因此,您可能希望将超时XHR请求包装到具有超时的WebWorker,以实现具有超时行为的类似异步XHR的方法。
德米特里·凯戈罗多夫

1
-1。这并不是一个很好的答案,关于线程的整个讨论只会使事情变得混乱。Sami下面的答案实际上要好得多。
Stijn de Witt 2015年

30

同步XHR对于保存用户数据很有用。如果您处理beforeunload事件,则可以在用户关闭页面时将数据上传到服务器。

如果使用async选项完成此操作,则该页面可能在请求完成之前关闭。同步执行此操作可确保请求按预期方式完成或失败。


4
编辑:我知道您只是从懒惰的HN用户转储txt,但是在此方面花一点时间很好地表述是值得的。
Frank Krueger

但这实际上是到目前为止我所看到的唯一正当理由。
Stijn de Witt

@Frank Krueger什么是HN?
道格拉斯于2015年

1
@DouglasHeld位于news.ycombinator.com的名为Hacker News的网站
Sami Samhuri 2015年

13

更新:

以下内容暗示-但交付失败-随着更好的异步请求处理的出现,确实没有理由使用同步请求,除非打算故意阻止用户在请求完成之前不做任何事情-听起来是恶意的: )

尽管这听起来可能很糟糕,但有时在用户离开页面之前或执行操作之前发生一个请求(或一系列请求)很重要-阻止其他代码执行(例如,防止后退按钮)可能对于设计不良的系统,可能会减少错误/维护;也就是说,我从未在野外看到它,并强调应该避免使用它。

promise一样,库通过回调链接过程来伪装同步。这适合大多数开发需求,其中需要有序的非阻塞事件,这些事件使浏览器能够保留对用户的响应能力(良好的UX)。

Mozilla文档所述,在某些情况下,您必须使用同步请求。但是,在这种情况下,也列出了使用信标(在IE / Safari中不可用)的解决方法。尽管这是实验性的,但如果达到标准接受水平,则可能会在同步请求棺材中钉上钉子。


您想在任何类似事务的处理中执行同步调用,或者在需要任何顺序的操作的地方执行同步调用。

例如,假设您要自定义事件以在播放歌曲后注销自己。如果首先发生注销操作,则该歌曲将永远不会播放。这需要同步请求。

另一个原因是使用WebService时,尤其是在服务器上执行数学运算时。

示例:服务器的变量值为1。

 Step (1) Perform Update: add 1 to variable
 Step (2) Perform Update: set variable to the power of 3
 End Value: variable equals 8

如果首先执行步骤(2),则结束值为2,而不是8;否则,结束值为2。因此操作顺序很重要,并且需要同步。


在一个常见的实际示例中,很少有理由可以进行同步调用。也许在单击登录名然后单击需要用户登录的网站的一部分时。

就像其他人所说的那样,它将束缚您的浏览器,因此请尽可能远离它。

但是,用户通常希望停止当前正在加载的事件,然后执行一些其他操作,而不是同步调用。在某种程度上,这是同步,因为第一个事件在第二个事件开始之前就已退出。为此,请在xml连接对象上使用abort()方法。


1
我认为这应该是真正的答案

3
@Zack:这是一个很好的示例,但是您可以对异步调用执行相同的操作,具体取决于调用它的事件。
vol7ron

3
+1在合理的情况下,异步服务器请求可能会导致逻辑竞争条件...但是,解决方案不必是同步的。除非从Web Worker处理您的请求,否则最好编写一个将请求编号的传输层,然后确保按请求ID的顺序处理操作。
罗伊·廷克

3
-1事务处理是一个完美的示例,您必须使用保证事物成功或失败的保证-仅仅因为命令是同步的,并不意味着第一个请求成功了……您依赖于告诉您的响应。在此示例中,您将在第一个请求的结果处理程序内执行第二个请求-在这种情况下,它们都可能也是异步的。
Mathew 2012年

2
@Mathew:只有在类似事务的过程依赖于其之前的操作结果时,这才是正确的。如果您的页面中有一个用于创建新对象(从服务器检索到)的按钮;如果是异步的,则用户可能会多次单击并返回许多对象。同步调用可以防止这种情况,并且仅允许在第一个新对象完成后创建另一个新对象,无论该对象是否出错。请注意措辞,我将其设为类似交易而非故意进行交易
vol7ron 2012年

8

我想说的是,如果您考虑在请求完成时阻止用户的浏览器,那么请确保使用同步请求。

如果请求序列化是您的目标,那么可以通过使上一个请求的onComplete回调触发下一个请求来使用异步请求来实现。


1
嗯,我要为此添加一个滑动窗口,否则,您基本上将失去每个请求的往返时间
Stephan Eggermont 2010年

8

在许多现实情况下,阻止UI正是所需的行为。

以具有多个字段的应用为例,某些字段必须通过对远程服务器的xmlhttp调用进行验证,以提供此字段的值和其他字段的值作为输入。

在同步模式下,逻辑很简单,用户遇到的阻塞非常短,没有问题。

在异步模式下,用户可以在验证初始字段时更改任何其他字段的值。这些更改将触发其他xmlhttp调用,其初始字段中的值尚未验证。如果初始验证失败怎么办?真是一团糟。如果不赞成并禁止同步模式,则应用程序逻辑将成为一场噩梦。基本上,必须重新编写应用程序以管理锁(例如,在验证过程中禁用其他项)。代码复杂度大大增加。否则可能会导致逻辑故障,并最终导致数据损坏。

基本上,问题是:什么是更重要的,无阻塞的UI体验或数据损坏的风险?答案应该留给应用程序开发人员,而不是W3C。


1
我认为您将阻止用户交互与阻止浏览器线程的想法混为一谈。有十亿个简单的方法可以防止用户在异步调用过程中继续与任何给定的实体(表单字段等)进行交互,而无需阻塞浏览器线程。当线程阻塞时,无论如何都不会进行任何逻辑处理,这将是一个糟糕的情况。它造成的问题/潜在问题实际上比解决的要多得多。
卢克·查弗斯

6

我可以看到用于同步XHR请求的用法,当必须先加载可变位置中的资源,然后页面中的其他静态资源(取决于第一个资源才能完全正常运行)时,才可以使用该请求。实际上,我在自己的一个子项目中实现了这样的XHR请求,而JavaScript资源则根据一组特定的参数位于服务器上的可变位置。后续的JavaScript资源依赖于那些可变资源,必须保证在加载其他相关文件之前先加载此类文件,从而使应用程序完整。

这个想法基础确实是在vol7ron的答案上扩展的。基于事务的过程实际上是唯一应该发出同步请求的时间。在大多数其他情况下,异步调用是更好的选择,其中,在调用之后,可以根据需要更新DOM。在许多情况下,例如基于用户的系统,您可以将某些功能锁定到“未经授权的用户”,直到他们本身登录为止。在异步调用之后,这些功能通过DOM更新过程解锁。

最后,我不得不说我同意大多数人的观点:在任何可能的情况下,都应避免同步XHR请求,因为按照它的工作方式,浏览器会锁定同步调用。在实现同步请求时,应该以通常会锁定浏览器的方式进行操作,无论如何,例如在实际发生页面加载之前在HEAD部分中。


一个动作依赖于另一个动作的结果这一事实无须同步执行另一个动作。仅当必须在调用函数终止之前处理某些操作的结果时,需要同步调用。
Stijn de Witt

5

自2015年起,桌面javascript应用程序变得越来越流行。通常在那些应用程序中,当加载本地文件时(使用XHR加载它们是一个非常有效的选择),加载速度如此之快,以至于几乎没有一点使异步代码过于复杂。当然,在某些情况下,可能会采用异步方式(从Internet请求内容,一次批量加载非常大的文件或大量文件),否则同步就可以了(并且更容易使用) 。


这实际上是我遇到的情况,现在是2020年。我有一个桌面应用程序,它通过HTTP显示其UI,因此您可以从在本地计算机甚至网络上运行的浏览器访问它。在这些情况下,网络是快速而可靠的,因此执行同步XHR请求是使代码保持简单的好方法。大多数人没有意识到异步XHR请求就像使用线程一样,并且可能会因为许多错误而变得非常复杂。
埃里克·穆塔

4

jQuery在某些情况下内部使用同步AJAX。插入包含脚本的HTML时,浏览器将不会执行它们。这些脚本需要手动执行。这些脚本可以附加点击处理程序。假定用户在附加了处理程序之前单击了某个元素,并且页面无法正常运行。因此,为了防止争用情况,将使用同步AJAX来获取那些脚本。由于同步AJAX有效地阻止了其他所有内容,因此可以确保脚本和事件以正确的顺序执行。


3

原因:

假设您有一个ajax应用程序,在用户进行任何交互之前,该应用程序需要进行六次http加载才能从服务器加载各种数据。

显然,您希望此事件从onload触发。

为此,同步调用可以很好地工作,而不会增加代码的复杂性。它很简单明了。

退税:

唯一的缺点是您的浏览器将锁定,直到加载所有数据或发生超时为止。对于有问题的ajax应用程序,这不是什么大问题,因为在所有初始数据都加载完之前,该应用程序没有用。

另类?

但是,当javascript繁忙时,许多浏览器会锁定所有窗口/选项卡,这是一个愚蠢的浏览器设计问题-但是,如果它阻止用户使用其他选项卡,那么阻止可能缓慢的网络访问就不太礼貌了在等待ajax页面加载时。

但是,无论如何,似乎同步获取已从最近的浏览器中删除或受限制。我不确定这是因为有人认为它们总是很糟糕,还是浏览器编写者对该主题的WC工作草案感到困惑。

http://www.w3.org/TR/2012/WD-XMLHttpRequest-20120117/#the-open-method确实使您看起来(请参阅第4.7.3节)使用阻塞模式时未设置超时。在我看来似乎很直观:每当有人阻止IO时,都应该设置合理的超时时间,因此,为什么允许阻止IO但不允许用户指定超时时间呢?

我的观点是,阻塞IO在某些情况下起着至关重要的作用,但必须正确实施。虽然不允许一个浏览器选项卡或窗口锁定所有其他选项卡或窗口,但这是浏览器设计的缺陷。可耻的地方要丢人。但是在某些情况下,个别选项卡或窗口在几秒钟内无响应(例如,使用阻塞的IO / HTTP GET)在几秒钟内无响应是完全可以接受的,例如,在页面加载时,可能有很多数据必须要做任何事情之前。有时正确实现的阻塞代码是最干净的方法。

当然,在这种情况下,可以使用异步http get获得等效功能,但是需要哪种愚蠢的例程?

我想我会尝试以下方法:

在加载文档时,请执行以下操作:1:设置6个全局“完成”标志变量,并将其初始化为0。2:执行所有6个后台获取(假设顺序无关紧要)

然后,每个6个http get的完成回调将设置它们各自的“ Done”标志。另外,每个回调将检查所有其他完成的标志,以查看是否已完成所有6个HTTP获取。看到所有其他对象都已完成后,要完成的最后一个回调将调用REAL初始化函数,该函数随后将设置所有内容,因为现在已全部提取了数据。

如果获取的顺序很重要-或网络服务器无法同时接受多个请求-那么您将需要以下内容:

在onload()中,将启动第一个http get。在回调中,第二个将被启动。在它的回调中,第三个-依此类推,依次类推,每个回调都会启动下一个HTTP GET。当最后一个返回时,它将调用实际的init()例程。


2

如果在生产代码中进行同步调用会怎样?

天空坠落。

不用说,用户不喜欢锁定的浏览器。


2
问题是“为什么使用XMLHTTPREQUEST”而不是“为什么不进行同步调用”
Itay Moav -Malimovka 2010年

1
如果您添加合理的超时时间,那应该不是问题。
格雷格

1
他要求使用同步调用的用例...而不是它们的缺点(我们现在都知道这些...)
Stijn de Witt

什么用户 所有用户都一样吗?工程师在本地测试如何?...所以访问文件系统的文件的延迟很小?
user2800679

2

在检查用户名不存在时,我用它来验证用户名。

我知道异步执行此操作会更好,但是对于该特定验证规则,我应该使用其他代码。我解释得更好。我的验证设置使用一些验证函数,这些函数将返回true或false,具体取决于数据是否有效。

由于该函数必须返回,因此我不能使用异步技术,因此我只是使它同步,并希望服务器能够及时响应而不至于太明显。如果我使用了AJAX回调,那么我将不得不以与其他验证方法不同的方式来处理其余的执行。


这种情况下的一个技巧是让验证功能仅检查一个标志(以开头false),并在服务器响应时将其设置为true或false。更改字段后,您将重置标志并再次调用服务器。
Stijn de Witt

2

有时您的行动取决于他人。例如,动作B仅在A完成后才能开始。同步方法通常用于避免竞争状况。有时,使用同步调用是一个更简单的实现,然后创建复杂的逻辑来检查彼此依赖的异步调用的每个状态。

这种方法的问题在于,您将“阻止”用户的浏览器,直到操作完成(直到请求返回,完成,加载等)为止。因此,使用它时要小心。


问题是“为什么要使用XMLHTTPREQUEST”而不是“为什么要进行同步调用”
Itay Moav -Malimovka 2010年

作者本人说:“同步调用会阻塞浏览器,从而导致糟糕的用户体验。” 在注释中,因此该线程中的每个人都申请了“同步调用”方法。如果您不能真正确定作者的意思,您为什么对问题有这样的字面理解?
GmonC 2010年

2

我在开发代码时使用了同步调用-在请求往返于服务器的往返期间所做的任何操作都可以掩盖错误的原因。

当它工作时,我将其设为异步,但是我尝试包括一个中止计时器和失败回调,因为您永远不知道...


2

SYNC与ASYNC:有什么区别?

基本上可以归结为:

console.info('Hello, World!');
doSomething(function handleResult(result) {
    console.info('Got result!');
});
console.info('Goodbye cruel world!');

如果doSomething同步的,这将打印:

Hello, World!
Got result!
Goodbye cruel world!

相反,如果doSomething异步,则将输出:

Hello, World!
Goodbye cruel world!
Got result!

由于该函数doSomething正在异步执行工作,因此它会在工作完成之前返回。所以我们只能在打印后得到结果Goodbye cruel world!

如果我们依赖于异步调用的结果,则需要将依赖的代码放在回调中:

console.info('Hello, World!');
doSomething(function handleResult(result) {
    console.info('Got result!');
    if (result === 'good') {
        console.info('I feel great!');
    }
    else {
        console.info('Goodbye cruel world!');
    }
});

因此,仅需要发生2到3件事就可以了,这并不需要同步(尽管大多数人更容易使用同步代码)。

为什么使用同步XMLHTTPREQUEST?

在某些情况下,您需要在调用的函数完成之前得到结果。考虑这种情况:

function lives(name) {
    return (name !== 'Elvis');
}
console.info('Elvis ' + (lives('Elvis') ? 'lives!' : 'has left the building...');

假设我们无法控制调用代码(该console.info行),并且需要更改函数lives来询问服务器...无法从内部向服务器发出异步请求,lives并且在lives完成之前仍然有我们的响应。因此我们不知道该返回true还是false。在函数完成之前获取结果的唯一方法是执行同步请求。

正如Sami Samhuri他在回答中提到的那样,在一个非常真实的场景中,您可能需要在函数终止之前对服务器请求做出答复onbeforeunload,因为这是应用程序中最后一个在关闭窗口之前运行的函数。

我不需要同步通话,但是无论如何我都可以使用它们

请不要 同步调用会锁定您的浏览器,并使该应用程序无响应。但是你是对的。异步代码更难。但是,有一种方法可以使处理起来更加容易。不像同步代码那么容易,但是它越来越接近:Promise s。

这是一个示例:在运行第三段代码之前,两个调用都应该成功完成:

var carRented = rentCar().then(function(car){
  gasStation.refuel(car);
});

var hotelBooked = bookHotel().then(function(reservation) {
  reservation.confirm();
});

Promise.all([carRented, hotelBooked]).then(function(){
  // At this point our car is rented and our hotel booked.
  goOnHoliday();
});

这是实现的方式bookHotel

function bookHotel() {
  return new Promise(function(resolve, reject){
    if (roomsAvailable()) {
      var reservation = reserveRoom();
      resolve(reservation);
    }
    else {
      reject(new Error('Could not book a reservation. No rooms available.'));
    }
  });
}

另请参阅:使用Promises编写更好的JavaScript


1
“请不要。同步调用会锁定您的浏览器,并使应用程序无响应。”这并不总是一个问题。假设您正在测试localhost,而您是一名开发人员。
user2800679

2

XMLHttpRequest传统上用于异步请求。有时(用于调试或特定的业务逻辑),您希望将一页中的所有/几个异步调用更改为同步。

您希望这样做而不更改JS代码中的所有内容。async / sync标志为您提供了这一功能,如果设计正确,则只需var在执行期间更改代码中的一行/更改一行的值即可。



1

我只是遇到这样一种情况:异步请求使用forEach(和for循环)连续调用的URL列表将导致其余请求被取消。我切换到同步,它们按预期工作。


1

在移动广告业务中,使用同步HTTP请求是一种常见的做法。

构建应用程序的公司(也称为“发布商”)通常会投放广告来产生收入。为此,他们将广告SDK安装到其应用中。存在许多内容(MoPub,Ogury,TapJob,AppNext,Google Ads AdMob)。

这些SDK将在网络视图中投放广告。

向用户投放广告时,必须要具有流畅的体验,尤其是在播放视频时。任何时候都不应有缓冲或加载。

为了解决这个问题precaching。媒体(图片/视频/等)在Webview的背景中同步加载的位置。

为什么不异步呢?

  1. 这是全球公认标准的一部分
  2. SDK会监听onload事件,以了解何时“准备好”将广告投放给用户

随着同步XMLHttpRequests的弃用,除非有其他方法可以确定,否则将来极有可能迫使广告业务更改标准。


0

同步XHR对于(非生产)内部工具和/或框架开发非常有用。想象一下,例如,您想在首次访问时同步加载代码库,如下所示:

get draw() 
{
    if (!_draw)
    {
       let file;
       switch(config.option)
       {
           case 'svg':
              file = 'svgdraw.js';
              break;
           case 'canvas':
              file = 'canvasdraw.js';
              break;
           default:
              file = 'webgldraw.js';
       }

       var request = new XMLHttpRequest();
       request.open('GET', file, false);
       request.send(null);
       _draw = eval(request.responseText);
    }

    return _draw;
}

在陷入头昏眼花,盲目反省邪恶的评估之前,请记住,这仅用于本地测试。对于生产版本,已设置_draw。

因此,您的代码可能如下所示:

foo.drawLib.draw.something(); //loaded on demand

这只是不同步XHR不可能完成的事情的一个示例。您可以预先加载此库(是的),或做一个Promise / Callback,但是如果没有同步XHR,就无法同步加载该库。考虑一下这种事情可以清理代码多少……

对工具和框架(在本地运行)可以执行的操作的限制仅受您的想象力限制。不过,似乎在JavaScript世界中想象力是有限的。


-1

好吧,这是一个很好的理由。我想发出一个http请求,然后根据结果在输入type = file上调用click()。对于异步xhr或fetch,这是不可能的。回调将丢失上下文“用户操作”,因此将忽略调用click()。同步xhr节省了我的培根。

onclick(event){
    //here I can, but I don't want to.
    //document.getElementById("myFileInput").click();
    fetch("Validate.aspx", { method : "POST", body: formData, credentials: "include" })
    .then((response)=>response.json())
    .then(function (validResult) {
        if (validResult.success) {
            //here, I can't.
            document.getElementById("myFileInput").click();
        }
    });
}

将其保存
Es Noguera
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.