是否有任何理由使用同步XMLHttpRequest?

时间:2010-01-18 18:36:31

标签: javascript ajax

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

20 个答案:

答案 0 :(得分:29)

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

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

答案 1 :(得分:26)

我认为随着HTML 5标准的进步,它们可能会变得更受欢迎。如果Web应用程序可以访问Web工作者,我可以预见开发人员使用专门的Web工作者来制作同步请求,正如Jonathan所说,确保一个请求发生在另一个请求之前。对于一个线程的当前情况,它是一个不太理想的设计,因为它阻塞直到请求完成。

答案 2 :(得分:13)

更新

以下暗示 - 但未能成功交付 - 随着更好的异步请求处理的出现,确实没有理由使用同步请求,除非打算故意阻止用户做任何事情直到请求完成 - 听起来很恶意:)

虽然这可能听起来很糟糕,但有时在用户离开页面之前或执行操作之前发生请求(或一系列请求)非常重要 - 阻止其他代码执行(例如,阻止其他代码执行)按钮)可能会减少设计不良的系统的错误/维护;那说,我从来没有在 wild 中看过它,并强调应该避免它。

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

作为stated in the Mozilla docs,您可能需要使用同步请求;但是,还列出了一种使用 beacon (在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;因此,操作顺序很重要,需要同步。


在一个普通的现实世界示例中,很少有同步调用可以被证明是合理的。也许在单击登录然后单击需要用户登录的站点部分时。

正如其他人所说,它会占用您的浏览器,因此请尽量远离它。

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

答案 3 :(得分:8)

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

如果请求序列化是您的目标,那么可以使用异步请求来完成此操作,方法是让您的上一个请求的onComplete回调触发下一行。

答案 4 :(得分:6)

答案 5 :(得分:5)

有许多现实世界的案例,阻止UI正是所期望的行为。

获取具有多个字段的应用程序,并且必须通过对远程服务器的xmlhttp调用来验证某些字段,并将此字段的值和其他字段值作为输入提供。

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

在异步模式下,用户可以在验证初始字段时更改任何其他字段的值。这些更改将触发其他xmlhttp调用,其中包含尚未验证的初始字段中的值。如果初始验证失败会怎样?纯洁的混乱。如果同意模式被弃用和禁止,应用程序逻辑将成为一个需要处理的噩梦。基本上,必须重新编写应用程序来管理锁(例如,在验证过程中禁用其他项)。代码复杂性大大增加。如果不这样做可能会导致逻辑失败并最终导致数据损坏。

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

答案 6 :(得分:4)

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

答案 7 :(得分:3)

截至2015年,桌面javascript应用程序正变得越来越流行。通常在这些应用程序中加载本地文件(并使用XHR加载它们是一个非常有效的选项)时,加载速度非常快,以至于使用异步使代码过于复杂。当然可能存在异步是要走的路(从互联网上请求内容,在一个批次中加载真正的大文件或大量文件),但是否则同步工作得很好(并且更容易使用)

答案 8 :(得分:2)

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

天空陨落。

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

答案 9 :(得分:2)

SYNC vs ASYNC:有什么区别?

基本上归结为:

siege -c10 -t60s http://127.0.0.1

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!

因此,只需要按顺序发生两到三件事就没有理由同步进行(虽然同步代码对大多数人来说更容易使用)。

为什么使用同步XMLHTTPREQUEST?

在某些情况下,您需要在被调用函数完成之前获得结果。请考虑以下情况:

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!');
    }
});

假设我们无法控制调用代码(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事件,因为它是最后一个函数来自您的应用程序将在关闭窗口之前运行。

我不需要同步电话,但我会使用它们,因为它们更容易

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

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

onbeforeunload

以下是如何实施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

另请参阅:Write Better JavaScript with Promises

答案 10 :(得分:2)

原因:

假设你有一个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 gets可以获得这种情况下的等效函数,但是需要什么样的高效例程呢?

我想我会尝试这些方法:

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

然后,6个http get的每一个的完成回调将设置它们各自的“完成”标志。 此外,每个回调都会检查所有其他已完成的标志,以查看是否所有6个HTTP都已完成。在看到所有其他人完成后,最后一个回调完成,然后调用REAL init函数,然后设置所有内容,现在数据全部被提取。

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

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

答案 11 :(得分:2)

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

您希望在不更改JS代码中的所有内容的情况下执行此操作。 async / sync标志为您提供了这种能力,如果设计正确,您只需更改代码中的一行/在执行期间更改一个var的值。

答案 12 :(得分:2)

我在开发代码时使用同步调用 - 无论你在服务器上下往服务器时做了什么都可以掩盖错误的原因。

当它工作时,我让它异步,但我尝试包含一个中止计时器和故障回调,因为你永远不知道...

答案 13 :(得分:2)

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

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

答案 14 :(得分:2)

在检查用户名不存在的情况下,我用它来验证用户名。

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

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

答案 15 :(得分:1)

Firefox(可能是所有非IE浏览器)不支持异步XHR timeOut。

  1. Stackoverflow discussion
  2. Mozilla Firefox XMLHttpRequest
  3. HTML5 WebWorkers支持超时。因此,您可能希望使用超时将同步XHR请求包装到WebWorker,以实现具有超时行为的类似异步的XHR。

答案 16 :(得分:1)

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

答案 17 :(得分:1)

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

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

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

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

要解决此问题,请使用precaching。媒体(图片/视频/等)在Webview的背景中同步加载的位置。

为什么不异步呢?

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

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

答案 18 :(得分:0)

这是一个很好的理由。我想做一个http请求,然后根据结果调用input 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();
        }
    });
}

答案 19 :(得分: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世界中想象力是有限的。