同步Ajax - Chrome在可信事件上是否有超时?

16

情况

我们有一个情况,需要在执行XHR / Ajax请求后通过单击打开新的浏览器标签页

我们通过将Ajax请求设置为同步执行来保持可信点击事件的上下文,并且这样做效果很好。

问题

然而,在最新的Chrome版本(36)中,当Ajax调用存在一些滞后时,我们会遇到弹出警告... 仅2秒的滞后就足以使Chrome显示弹出警告,而不是像应该的那样打开标签页。代码本身是有效的,我可以多次点击该按钮,并且除了请求遇到一些滞后外,它始终有效。然后我得到了弹出警告...

问题

在同步Ajax请求期间是否应用了超时,需要在此期间完成以使受信任的事件仍然可用?

是否有任何方法可以规避这种情况?毕竟,通话已经同步并冻结直到结果到达。

谢谢。

更新JSFiddle

更新:我创建了一个JSFiddle来演示问题:http://jsfiddle.net/23JNw/9/

/**
* This method will give open the popup without a warning.
*/
function performSlowSyncronousRequest() {
    $.ajax({
     url: '/echo/html',
     data: {delay: 2}, //JSfiddle will delay the answer by 2 seconds
     success: function(){
         window.open('http://www.thirtykingdoms.com'); //this causes the popup warning in Chrome
     },
     async: false
    });
}

有没有修好这个问题的运气?我也遇到了同样的问题,并确定超时时间为1000毫秒。超过这个时间会导致Chrome阻塞。 - Alexander
同步请求会阻塞JS执行,所以你看到的警告可能是说你的脚本没有响应。如果请求等待太长时间才收到响应,你就会看到这个警告。尝试进行异步请求,并在成功回调中启用按钮以打开选项卡。 - Rubens Pinheiro
1
@Rubens:感谢您的评论,但是:不是这样的。警告是一个清晰的弹出式警告,就像我在问题中描述的那样。此外,禁用和启用按钮需要用户点击两次才能打开弹出窗口,这当然很容易,但不是我们想要的。我们希望通过单个可信事件进行后台请求并打开弹出窗口。 - Christopher Lörken
我现在明白了,谢谢你的示例。嗯,很难说,也许如果脚本等待太久才响应,Chrome会使函数上下文不受信任。 我会尝试找到解决这个问题的方法 :) - Rubens Pinheiro
3个回答

6
可能可以解决这个问题的方法是在XHR请求返回之前,在您仍处于受信任的上下文中打开新标签页。通过Javascript打开的浏览器标签页和窗口与父窗口保持连接,可以互相通信。
如果您在单击链接时打开一个新标签页,您可以在新窗口中显示加载屏幕,同时运行XHR调用。这种工作流程不像您最初的请求那样干净,但经过一些思考,它将是可行的解决方案。下面的脚本只是使用window.setTimeout()来模拟异步XHR请求的快速示例。
<html>
<body>
    <h4>
    Hello
    </h4>
    <a id="openWindow" href="">Make http call and open window.</a>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
    <script>
        (function ($) {
            var newWindow = null,
                timeout = null;

          $(document).ready(function () {
            $('#openWindow').on('click', function (evt) {
                evt.preventDefault();

              newWindow = window.open('about:blank', 'tempWindow');
              $(newWindow.document).find('body').append('<div class="loading">Loading...</div>');

              timeout = window.setTimeout(function () {
                // simulates async XHR
                $(newWindow.document).find('.loading').remove();
                $(newWindow.document).find('body').append('Done loading, here\'s your data');

              }, 5000)

            });
          });

        }(jQuery));
    </script>
</body>


我认为从用户体验来看,这并不完美,因为在出现错误时,弹出窗口必须再次关闭,但这仍然是我们能想到的最好的解决方案,也是我们目前采取的方法。因此,我将把它标记为已接受的答案。感谢提供示例代码。 - Christopher Lörken
这对我似乎有效。在JSON返回后,我能够更改newwindow.location并加载想要的页面。 - John Lord

0

你的问题不在于 XMLHttpRequest,而是在于 delay(同步延迟,可能是 WebKit/Blink 中的 bug)

请参考示例(http://jsfiddle.net/23JNw/32/ Snippet 中的沙盒不允许弹出窗口):

function performSlowSyncronousRequest() {
    var endsIn, initial;

    delay = 5000;

    endsIn = new Date().getTime() + delay;

    for (; endsIn >= new Date().getTime();) {}//Delay
    window.open('http://www.thirtykingdoms.com');
}

<button onclick="performSlowSyncronousRequest()">Test case</button>

注意:一些浏览器认为(XMLHttpRequest同步)已经过时,这对用户体验非常不好。
我尝试模拟点击,但没有起作用:

function clickFire(evt){
 var el, evtFake, pos;

 el = document.createElement("a");
    el.href = "javascript:void(0);";
    el.innerHTML = "test";
 el.onclick = evt;

 document.body.appendChild(el);

 pos = el.getBoundingClientRect();

 evtFake = new MouseEvent("click", {
  bubbles: false, 
  cancelable: false,
  view: window,
  detail: 0,
  screenX: window.screenX,
  screenY: window.screenY,
  clientX: pos.left + 1,
  clientY: pos.top + 1,
  ctrlKey: false,
  shiftKey: false,
  altKey: false,
  metaKey: false,
  button: 1,
  buttons: 0,
  relatedTarget: el
 });
 el.dispatchEvent(evtFake);

 window.setTimeout(function() {
  document.body.removeChild(el);
 }, 1);
}

window.setTimeout(function() {
 clickFire(function() {
  window.open("http://stackoverflow.com");
 });
}, 1000);

注意:现在的网络浏览器非常聪明,我们几乎无法欺骗它们。
解决方案:
不要使用弹出窗口(我讨厌弹出窗口: )),尝试使用

好的,答案是“是的,Chrome在可信事件上有超时”。我早就有所怀疑,但这仍然不够好。您提出的伪造弹出窗口的建议不可行,因为我不是在谈论页面内的弹出窗口,而是指打开新标签页。我们有重型单页JavaScript应用程序,需要在新标签页中打开多个实例。我唯一看到的解决方案是摆脱sjax调用,在后台以某种方式完成它的工作... - Christopher Lörken
@ChristopherLörken 尝试使用模态框(如Bootstrap)添加一个按钮,并放置一条消息询问用户是否单击? - Guilherme Nascimento

0

你好@ChristopherLörken。 你能给出一个代码示例或一个fiddle吗?也许我没有理解你想要什么。

我认为这会有所帮助:
如果您需要在上下文中使用事件,可以保存事件的引用以供后续使用,例如在回调中。 使用jQuery的示例:

$(myBtn).click(function(ev){
   var event = ev; //Save the event object reference
   $.ajax({
   // ... your options
   success: function(res){
     //do stuff with the event in the callback
     console.log(event);
   });
});

这样,您就不需要调用同步请求来在上下文中使用事件,并且作为异步请求,Chrome也不会抱怨。 :)


1
谢谢,我已经准备了一个JSFiddle来演示这个问题。关键是,即使我在代码中记住了事件,我也不在浏览器需要打开弹出窗口而不出现警告的“受信任事件”上下文中。该fiddle链接在问题中。 - Christopher Lörken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接