有什么理由使用同步XMLHttpRequest?
似乎大多数人都使用XMLHttpRequest进行asynchronous请求,但显然有能力做同步请求的事实表明可能有一个合理的理由这样做。 那么这个合理的原因是什么呢?
我认为随着HTML 5标准的进展,它们可能会变得越来越stream行。 如果一个Web应用程序可以访问多个线程,我可以预见开发人员可以使用一个专用线程来发出同步请求,正如Jonathan所说,确保一个请求在另一个请求之前发生。 对于一个线程的当前情况来说,这是一个不太理想的devise,因为它阻塞了,直到请求完成。
同步XHR对保存用户数据非常有用。 如果处理beforeunload
事件,则可以在用户closures页面时将数据上载到服务器。
如果这是使用asynchronous选项完成的,那么在请求完成之前页面可能会closures。 这样做同步确保请求完成或以预期的方式失败。
更新:
下面暗示了 – 但是并没有成功交付 – 随着更好的asynchronous请求处理的出现,实际上没有理由使用同步请求,除非打算在请求完成之前故意阻止用户做任何事情 – 听起来是恶意的: )
虽然这可能听起来很糟糕,但在用户离开页面之前,或在执行某个操作之前,可能有时候发生请求(或一系列请求)是非常重要的 – 阻止其他代码执行(例如阻止后退button)可能会可能减lessdevise不良的系统的错误/维护; 说,我从来没有在野外看到它,并强调应该避免。
像诺言一样,图书馆通过callback链接进程假装同步。 这适合于大多数需要定制的非阻塞事件,使浏览器能够保持对用户的响应(良好的用户体验)。
正如在Mozilla文档中所述 ,有些情况下您必须使用同步请求; 不过,也列出了这种情况下使用信标 (在IE / Safari中不可用)的解决方法。 虽然这是实验性的,但如果它达到标准的接受程度,它可能会在同步请求棺材里钉上一个钉子。
您希望在任何类似事务的处理中执行同步调用,或者需要任何操作顺序的地方执行同步调用。
例如,假设您想要自定义一个事件以在播放歌曲后将其注销。 如果注销操作首先发生,那么歌曲将永远不会播放。 这需要同步请求。
另一个原因是使用WebService时,特别是在服务器上执行math运算时。
示例:服务器有一个值为1的variables。
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,而不是8; 因此操作的顺序和同步是必要的。
在同一个现实世界的例子中,很less有同步调用是合理的。 也许当点击login,然后点击需要用户login的网站的一部分。
正如其他人所说,它会捆绑您的浏览器,所以你可以远离它。
但是,不是同步调用,通常用户想要停止当前正在加载的事件,然后执行其他操作。 在某种程度上这是同步,因为第一个事件在第二个开始之前退出。 为此,请在xml连接对象上使用abort()方法。
我想说,如果考虑在请求完成时阻止用户的浏览器,那么请确保使用同步请求。
如果请求序列化是你的目标,那么可以通过使用asynchronous请求来完成,这是通过让你先前请求的onCompletecallback触发下一个请求。
当可变位置中的资源必须先加载到依赖第一个资源才能完全工作的页面中的其他静态资源之前时,才能看到使用同步XHR请求的用法。 事实上,我正在自己的一个小子项目中实现这样一个XHR请求,而JavaScript资源则驻留在服务器上的不同位置,这取决于一组特定的参数。 随后的JavaScript资源依赖于这些variables资源,这些文件必须保证在加载其他依赖文件之前加载,从而使应用程序完整。
这个想法的基础确实有点扩展到volonron的答案。 基于事务的过程实际上是同步请求的唯一时间。 在大多数情况下,asynchronous调用是更好的select,在调用之后,根据需要更新DOM。 在许多情况下,例如基于用户的系统,您可能会将某些functionlocking到“未经授权的用户”,直到他们本身login。asynchronous调用后,这些function通过DOM更新过程解锁。
我不得不说,我同意大多数人关于这个问题的观点:只要有可能,应该避免同步的XHR请求,因为它的工作方式是浏览器locking同步调用。 在实现同步请求时,应该以浏览器通常被locking的方式完成,无论如何,在实际发生页面加载之前的HEAD部分中。
有很多现实世界的情况下,阻止用户界面正是所需的行为。
带有多个字段的应用程序,一些字段必须通过xmlhttp调用到远程服务器进行validation,该远程服务器将该字段的值和其他字段值作为input。
在同步模式下,逻辑简单,用户遇到的阻塞很短,没有问题。
在asynchronous模式下,用户可以更改任何其他字段的值,而最初的一个正在validation。 这些更改将触发其他xmlhttp调用,初始字段的值尚未validation。 如果初始validation失败会怎样? 纯粹的混乱。 如果同步模式被弃用和禁止,应用程序逻辑就成为一个噩梦来处理。 基本上,应用程序必须重新编写来pipe理locking(例如,在validation过程中禁用其他项目)。 代码复杂度大大增加。 否则可能会导致逻辑失败并最终导致数据损坏。
基本上问题是:什么是更重要的,非阻塞的用户体验或数据损坏的风险? 答案应该留在应用程序开发者,而不是W3C。
在某些情况下,jQuery在内部使用同步AJAX。 插入包含脚本的HTML时,浏览器将不会执行它们。 脚本需要手动执行。 这些脚本可能会附加点击处理程序。 假设用户在附加处理程序之前单击某个元素,并且该页面不能按预期运行。 因此,为了防止竞争条件,将使用同步AJAX来获取这些脚本。 由于同步AJAX有效地阻止了其他事情,因此可以确定脚本和事件按照正确的顺序执行。
截至2015年,桌面的JavaScript应用程序变得越来越stream行。 通常在这些应用程序中加载本地文件(使用XHR加载它们是一个非常有效的选项)时,加载速度非常快,以至于使代码与asynchronous过于复杂。 当然,有些情况下可能需要asynchronous(从互联网上请求内容,在一个批次中加载真正的大文件或大量的文件),但是否则同步工作就会很好(而且使用起来更容易) 。
如果在生产代码中进行同步调用会发生什么?
天空倒塌。
不严重,用户不喜欢locking浏览器。
我用它来validation用户名,在检查用户名不存在。
我知道最好是asynchronous执行,但是我应该为这个特定的validation规则使用不同的代码。 我解释得更好。 我的validation设置使用一些validation函数,返回true或false,取决于数据是否有效。
由于函数必须返回,我不能使用asynchronous技术,所以我只是做同步,希望服务器能够及时回答不要太明显。 如果我使用AJAXcallback,那么我将不得不处理剩下的执行与其他validation方法不同。
有时你有一个取决于别人的行动。 例如,动作B只能在A完成时启动。 同步方法通常用于避免竞态条件 。 有时使用同步调用是一个更简单的实现,然后创build复杂的逻辑来检查asynchronous调用的相互依赖的每个状态。
这种方法的问题是,你“阻止”用户的浏览器,直到动作完成(直到请求返回,完成,加载等)。 所以在使用时要小心。
我在开发代码的时候使用了同步调用,无论你做什么,请求在服务器和服务器之间通勤都可以掩盖错误的原因。
当它工作时,我使它asynchronous,但我尝试包括中止计时器和失败callback,因为你永远不知道…
原因:
比方说,你有一个Ajax应用程序需要做六个http获取从服务器加载各种数据之前,用户可以做任何交互。
显然你想要这个从onload触发。
同步调用对此没有任何额外的复杂性。 这很简单,直接。
退税:
唯一的缺点是你的浏览器locking,直到所有的数据加载或超时发生。 至于ajax应用程序,这不是一个问题,因为应用程序是无用的,直到所有的初始数据加载无论如何。
另类?
然而,许多浏览器locking所有的窗口/标签时,其中的任何一个繁忙的JavaScript,这是一个愚蠢的浏览器devise问题 – 但因为阻止可能慢networking获取是不礼貌的,如果它让用户不使用其他标签同时等待ajax页面加载。
但是,它看起来像同步获取已被删除或限制从最近的浏览器无论如何。 我不确定这是因为有人认为他们总是不好,或者浏览器作者被WC工作草案弄糊涂了。
http://www.w3.org/TR/2012/WD-XMLHttpRequest-20120117/#the-open-method的确使它看起来像(见4.7.3节)在使用阻塞模式时,你不能设置超时。; 对我来说这似乎是直观的:每当阻止IO时,设置一个合理的超时是有礼貌的,那么为什么允许阻塞io,而不是用户指定的超时?
我的观点是阻止IO在某些情况下有着至关重要的作用,但必须正确实施。 虽然一个浏览器选项卡或窗口locking所有其他选项卡或窗口是不可接受的,但这是浏览器devise缺陷。 在羞耻到期的时候羞愧。 但在某些情况下,单个标签页或窗口在某些情况下(例如,在页面加载时可能会使用大量数据)无法响应几秒钟(例如,使用阻止IO / HTTP GET)无论如何要做任何事情之前都需要做好准备。 有时,正确实施的阻止代码是最干净的方式。
当然,在这种情况下,等价的函数可以使用asynchronoushttp获取,但需要什么样的愚蠢程序?
我想我会尝试这些方面的东西:
在文件加载时,执行以下操作:1:设置6个全局“完成”标志variables,初始化为0. 2:执行全部6个背景获取(假设顺序无关紧要)
然后,6个http get的每个完成callback将设置它们各自的“完成”标志。 此外,每个callback将检查所有其他完成标志,看看是否所有的6个HTTP已完成。 最后一个callback完成后,看到所有其他人已完成,然后将调用REAL初始化函数,然后将设置一切,现在的数据全部取出。
如果抓取的顺序很重要 – 或者Web服务器不能同时接受多个请求 – 那么你需要这样的东西:
在onload()中,第一个http get将被启动。 在callback中,第二个将会启动。 在它的callback,第三个等等,每个callback启动下一个HTTP GET。 当最后一个返回时,它会调用真正的init()例程。
XMLHttpRequest
传统上用于asynchronous请求。 有时(对于debugging或特定的业务逻辑),您希望将一个页面中的所有/几个asynchronous调用更改为同步。
你想在不改变你的JS代码的一切。 asynchronous/同步标志提供了这种能力,如果devise正确,只需要在代码中更改一行/在执行期间更改一个variables的值。
Firefox(可能所有非IE浏览器)不支持asynchronousXHR超时。
- Stackoverflow讨论
- Mozilla Firefox XMLHttpRequest
HTML5 WebWorkers支持超时。 因此,您可能想要将带有超时的XHR请求同步到WebWorker,以实现具有超时行为的asynchronousXHR。
我刚刚遇到了一个情况:使用forEach(和for循环)连续调用的url列表的asynchronous请求将导致其余的请求被取消。 我切换到同步,他们按预期工作。
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
是asynchronous的 ,则会打印:
Hello, World! Goodbye cruel world! Got result!
因为函数doSomething
在asynchronous工作,所以在工作完成之前它会返回。 所以我们只有在打印Goodbye cruel world!
后才能得到结果Goodbye cruel world!
如果我们依赖于asynchronous调用的结果,则需要将相关的代码放在callback中:
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!'); } });
因此,事实上,需要按顺序发生两三件事是没有道理的,尽pipe同步代码对于大多数人来说更容易处理。
为什么使用同步XMLHTTPREQUEST?
在某些情况下,在调用函数完成之前需要结果。 考虑这种情况:
function lives(name) { return (name !== 'Elvis'); } console.info('Elvis ' + (lives('Elvis') ? 'lives!' : 'has left the building...');
假设我们无法控制调用代码( console.info
行),需要更改函数lives
以询问服务器…我们无法从lives
对服务器执行asynchronous请求,仍然有我们的回应在lives
完成之前。 所以我们不知道是否返回true
或false
。 在函数完成之前获得结果的唯一方法是通过执行同步请求。
正如Sami Samhuri
在他的回答中提到的,在函数终止之前,您可能需要服务器请求的答案的一个非常真实的场景是onbeforeunload
事件,因为它是应用程序的最后一个函数,在窗口closures之前运行。
我不需要同步呼叫,但我使用他们,因为他们更容易
请不要。 同步通话会locking您的浏览器,导致应用程序无法响应。 但是你是对的。 asynchronous代码更难。 但是,有一种方法可以更容易处理。 并不像同步代码那么简单,但它正在接近: Promise s。
这是一个例子:两个asynchronous调用应该在第三段代码可以运行之前成功完成:
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.')); } }); }
另请参阅: 用承诺编写更好的JavaScript 。