【问题标题】:Synchronous Ajax - does Chrome have a timeout on trusted events?同步 Ajax - Chrome 是否对可信事件有超时?
【发布时间】:2014-08-22 11:38:59
【问题描述】:

情况

我们有一种情况,在执行 XHR / Ajax 请求后,我们需要onclick-open a new tab 在浏览器中

我们通过将 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 被阻止。
  • 同步请求会阻止 JS 执行,因此您看到的警告可能是您的脚本没有响应。如果请求等待响应太久,你会看到这个警告。尝试执行异步请求并在成功回调上启用按钮以打开选项卡。
  • @Rubens:感谢您的评论,但是:不。正如我在问题中所描述的那样,警告是一个明确的弹出警告。此外,禁用和启用按钮需要用户单击两次才能打开弹出窗口,这当然很容易,但不是我们想要的。我们想做一个后台请求并打开一个带有单个可信事件的弹出窗口。
  • 我现在低估了,谢谢你的小提琴。好吧,很难说,如果脚本等待太久才响应,Chrome 可能会将函数上下文变为不可信。我会尝试找到解决这个问题的方法:)

标签: javascript ajax google-chrome popup synchronous


【解决方案1】:

解决此问题的方法是在 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>

【讨论】:

  • 我认为从用户体验来看,这并不完全好,因为如果发生错误,必须再次关闭弹出窗口,但这也是我们能想出的最佳解决方案。我们现在结束了。因此,我将其标记为已接受的答案。感谢您提供示例代码。
  • 这似乎对我有用。我能够更改 newwindow.location 并在 json 返回后加载我想要的页面。
【解决方案2】:

你好@ChristopherLörken。你能给出一个示例代码或你在做什么的小提琴吗?也许我不明白你想要什么。

我认为这会有所帮助:
如果您需要在您的上下文中使用该事件,您可以保存该事件的引用以供以后使用,例如在回调中。 使用 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 不会对此抱怨。 :)

【讨论】:

  • 谢谢,我已经整理了一个 JSFiddle 来演示这个问题。关键是,即使我记得代码中的事件,我也离开了“可信事件”上下文,浏览器需要在没有警告的情况下打开弹出窗口。问题中链接了小提琴。
【解决方案3】:

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

查看示例(代码段中的http://jsfiddle.net/23JNw/32/ 沙盒不允许弹出窗口):

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>

注意:某些浏览器认为 sjax(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);

注意:当今的网络浏览器非常智能,我们几乎不会被欺骗。

解决方案

不要使用弹出窗口(我讨厌弹出窗口 :)),尝试使用 &lt;iframe&gt; 模拟“弹出窗口”:http://demos.jquerymobile.com/1.4.0/popup-iframe/

或使用模态(如引导程序)添加 um 按钮并放置一条消息要求用户点击:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" rel="stylesheet"/>

<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="exampleModalLabel">New message</h4>
      </div>
      <div class="modal-body">
         Open pop-up :)
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
        <button id="popupIsNotPopular" type="button" class="btn btn-primary">Ok</button>
      </div>
    </div>
  </div>
</div>

window.setTimeout(function() {
    $('#exampleModal').modal();
}, 2000);


$("#popupIsNotPopular").click(function() {
    window.open("http://www.stackoverflow.com");
});

【讨论】:

  • 好的,所以答案是“是的,chrome 在可信事件上有超时”。我很怀疑,但它仍然不好。您对伪造弹出窗口的建议不是一个选项,因为我不是在谈论我的页面中的弹出窗口,我说的是打开新标签。我们有大量的单页 Javascript 应用程序,我们需要在新选项卡中打开多个实例。我看到的唯一解决方案是我摆脱了 sjax 调用,在后台以某种方式执行它......
  • @ChristopherLörken 尝试使用模式添加 um 按钮(如引导程序)并放置一条消息询问用户点击?
猜你喜欢
  • 2011-01-19
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多