似乎大多数人都使用XMLHttpRequest进行异步请求,但是很明显,可以执行同步请求的事实表明这样做可能是有正当理由的。那么,那个合理的原因可能是什么呢?
似乎大多数人都使用XMLHttpRequest进行异步请求,但是很明显,可以执行同步请求的事实表明这样做可能是有正当理由的。那么,那个合理的原因可能是什么呢?
Answers:
我认为随着HTML 5标准的发展,它们可能会变得越来越流行。如果将Web应用程序授予Web工作人员访问权限,我可以预见,开发人员将使用专用的Web工作人员发出同步请求,如乔纳森所说,以确保一个请求先于另一个请求发生。在当前只有一个线程的情况下,这是一种不太理想的设计,因为它会阻塞直到请求完成。
同步XHR对于保存用户数据很有用。如果您处理beforeunload
事件,则可以在用户关闭页面时将数据上传到服务器。
如果使用async选项完成此操作,则该页面可能在请求完成之前关闭。同步执行此操作可确保请求按预期方式完成或失败。
以下内容暗示-但交付失败-随着更好的异步请求处理的出现,确实没有理由使用同步请求,除非打算故意阻止用户在请求完成之前不做任何事情-听起来是恶意的: )
尽管这听起来可能很糟糕,但有时在用户离开页面之前或执行操作之前发生一个请求(或一系列请求)很重要-阻止其他代码执行(例如,防止后退按钮)可能对于设计不良的系统,可能会减少错误/维护;也就是说,我从未在野外看到它,并强调应该避免使用它。
像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()方法。
我想说的是,如果您考虑在请求完成时阻止用户的浏览器,那么请确保使用同步请求。
如果请求序列化是您的目标,那么可以通过使上一个请求的onComplete回调触发下一个请求来使用异步请求来实现。
在许多现实情况下,阻止UI正是所需的行为。
以具有多个字段的应用为例,某些字段必须通过对远程服务器的xmlhttp调用进行验证,以提供此字段的值和其他字段的值作为输入。
在同步模式下,逻辑很简单,用户遇到的阻塞非常短,没有问题。
在异步模式下,用户可以在验证初始字段时更改任何其他字段的值。这些更改将触发其他xmlhttp调用,其初始字段中的值尚未验证。如果初始验证失败怎么办?真是一团糟。如果不赞成并禁止同步模式,则应用程序逻辑将成为一场噩梦。基本上,必须重新编写应用程序以管理锁(例如,在验证过程中禁用其他项)。代码复杂度大大增加。否则可能会导致逻辑故障,并最终导致数据损坏。
基本上,问题是:什么是更重要的,无阻塞的UI体验或数据损坏的风险?答案应该留给应用程序开发人员,而不是W3C。
我可以看到用于同步XHR请求的用法,当必须先加载可变位置中的资源,然后页面中的其他静态资源(取决于第一个资源才能完全正常运行)时,才可以使用该请求。实际上,我在自己的一个子项目中实现了这样的XHR请求,而JavaScript资源则根据一组特定的参数位于服务器上的可变位置。后续的JavaScript资源依赖于那些可变资源,必须保证在加载其他相关文件之前先加载此类文件,从而使应用程序完整。
这个想法基础确实是在vol7ron的答案上扩展的。基于事务的过程实际上是唯一应该发出同步请求的时间。在大多数其他情况下,异步调用是更好的选择,其中,在调用之后,可以根据需要更新DOM。在许多情况下,例如基于用户的系统,您可以将某些功能锁定到“未经授权的用户”,直到他们本身登录为止。在异步调用之后,这些功能通过DOM更新过程解锁。
最后,我不得不说我同意大多数人的观点:在任何可能的情况下,都应避免同步XHR请求,因为按照它的工作方式,浏览器会锁定同步调用。在实现同步请求时,应该以通常会锁定浏览器的方式进行操作,无论如何,例如在实际发生页面加载之前在HEAD部分中。
自2015年起,桌面javascript应用程序变得越来越流行。通常在那些应用程序中,当加载本地文件时(使用XHR加载它们是一个非常有效的选择),加载速度如此之快,以至于几乎没有一点使异步代码过于复杂。当然,在某些情况下,可能会采用异步方式(从Internet请求内容,一次批量加载非常大的文件或大量文件),否则同步就可以了(并且更容易使用) 。
原因:
假设您有一个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()例程。
如果在生产代码中进行同步调用会怎样?
天空坠落。
不用说,用户不喜欢锁定的浏览器。
在检查用户名不存在时,我用它来验证用户名。
我知道异步执行此操作会更好,但是对于该特定验证规则,我应该使用其他代码。我解释得更好。我的验证设置使用一些验证函数,这些函数将返回true或false,具体取决于数据是否有效。
由于该函数必须返回,因此我不能使用异步技术,因此我只是使它同步,并希望服务器能够及时响应而不至于太明显。如果我使用了AJAX回调,那么我将不得不以与其他验证方法不同的方式来处理其余的执行。
false
),并在服务器响应时将其设置为true或false。更改字段后,您将重置标志并再次调用服务器。
有时您的行动取决于他人。例如,动作B仅在A完成后才能开始。同步方法通常用于避免竞争状况。有时,使用同步调用是一个更简单的实现,然后创建复杂的逻辑来检查彼此依赖的异步调用的每个状态。
这种方法的问题在于,您将“阻止”用户的浏览器,直到操作完成(直到请求返回,完成,加载等)为止。因此,使用它时要小心。
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.'));
}
});
}
XMLHttpRequest
传统上用于异步请求。有时(用于调试或特定的业务逻辑),您希望将一页中的所有/几个异步调用更改为同步。
您希望这样做而不更改JS代码中的所有内容。async / sync标志为您提供了这一功能,如果设计正确,则只需var
在执行期间更改代码中的一行/更改一行的值即可。
Firefox(可能还有所有非IE浏览器)不支持异步XHR超时。
HTML5 WebWorkers确实支持超时。因此,您可能希望将XHR请求与超时包装到WebWorker,以实现具有超时行为的类似异步XHR。
在移动广告业务中,使用同步HTTP请求是一种常见的做法。
构建应用程序的公司(也称为“发布商”)通常会投放广告来产生收入。为此,他们将广告SDK安装到其应用中。存在许多内容(MoPub,Ogury,TapJob,AppNext,Google Ads AdMob)。
这些SDK将在网络视图中投放广告。
向用户投放广告时,必须要具有流畅的体验,尤其是在播放视频时。任何时候都不应有缓冲或加载。
为了解决这个问题precaching
。媒体(图片/视频/等)在Webview的背景中同步加载的位置。
为什么不异步呢?
onload
事件,以了解何时“准备好”将广告投放给用户随着同步XMLHttpRequests的弃用,除非有其他方法可以确定,否则将来极有可能迫使广告业务更改标准。
同步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世界中想象力是有限的。
好吧,这是一个很好的理由。我想发出一个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();
}
});
}