不可能的内联 Javascript 延迟/睡眠
有一个JavaScript函数,我对其代码的控制为零,它调用了我编写的函数。我的函数使用 DOM 生成 iFrame,定义它的 src,然后将其附加到另一个 DOM 元素。然而,在我的函数返回并允许继续执行包含的函数之前,必须完全加载 iFrame。
以下是我尝试过的方法以及它们不起作用的原因:
1。 SetTimeout 选项:
99.999% 的情况下,这就是答案。事实上,在我指导 JavaScript 的过去十年里,我一直坚持认为代码总是可以重构来使用这个选项,并且从不相信存在不是这种情况的情况。嗯,我终于找到了!问题是,因为我的函数被内联调用,如果在 iFrame 完成加载之前执行下一行,它会完全中性化我的脚本,并且从我的脚本完成的那一刻起,外部脚本就会继续。某种回调将不起作用
2。 “不执行任何操作”循环:
您使用此选项 while(//iFrame 未加载){//不执行任何操作}。理论上,直到框架加载后才会返回。问题是,由于这会占用所有资源,因此 iFrame 永远不会加载。这个技巧虽然非常不专业、肮脏等,但当你只需要内联延迟时,它就会起作用,但由于我需要一个外部线程来完成,所以它不会。
在 FF 中,几秒钟后,它会暂停脚本并弹出警报,指出存在无响应的脚本。当该警报发出时,iFrame 能够加载,然后我的函数能够返回,但是浏览器冻结 10 秒,然后要求用户正确消除错误是行不通的。
3.模型对话:
我受到以下事实的启发:FF 弹出窗口允许加载 iFrame,同时停止函数的执行,并且思考一下,我意识到这是因为模态对话是一种停止执行但允许其他线程继续执行的方式!太棒了,所以我决定尝试其他模式选项。像alert()这样的东西工作得很好!当它弹出时,即使只弹出 1/10 秒,iFrame 也能完成,并且一切都很好。为了防止 1/10 秒不够,我可以将模型对话放入解决方案 2 中的 while 循环中,这样可以确保 iFrame 及时加载。甜甜的吧?除了我现在必须弹出一个非常不专业的对话框供用户忽略才能运行我的脚本这一事实。我和自己争论过这个动作的成本/收益,但后来我遇到了一个场景,我的代码在一个页面上被调用了 10 次!在访问页面之前必须消除 10 个警报?!这让我想起了 90 年代末的脚本小子页面,但不是一个选项。
4.还有无数其他延迟脚本:
大约有 10 个 jQuery 延迟或睡眠函数,其中一些实际上开发得相当巧妙,但没有一个起作用。有一些原型选项,但我发现没有一个可以做到这一点!大约十几个其他库和框架声称它们拥有我需要的东西,但可惜的是,它们都合谋给了我虚假的希望。
我确信,由于内置模型对话可以停止执行,同时允许其他线程继续,因此必须有一些代码可访问的方法来执行相同的操作,而无需用户输入。
该代码实际上有成千上万行并且是专有的,因此我编写了这个问题的小示例供您使用。重要的是要注意,您可以更改的唯一代码位于 onlyThingYouCanChange 函数
测试文件:
<html>
<head>
</head>
</html>
<body>
<div id='iFrameHolder'></div>
<script type='text/javascript'>
function unChangeableFunction()
{
new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
new_iFrame_body = new_iFrame_doc.body;
if(new_iFrame_body.innerHTML != 'Loaded?')
{
//The world explodes!!!
alert('you just blew up the world! Way to go!');
}
else
{
alert('wow, you did it! Way to go!');
}
}
var iFrameLoaded = false;
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
unChangeableFunction();
</script>
</body>
blank_frame.html 中:
<html>
<head>
</head>
<body style='margin:0px'>Loaded?</body>
</html>
HERE IS THE ANSWER I MADE FROM COMBINING IDEAS FROM RESPONDERS! YOU GUYS ROCK!
new source of the function I was allowed to change :
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
if (window.XMLHttpRequest)
{
AJAX=new XMLHttpRequest();
}
else
{
AJAX=new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX)
{
AJAX.open("GET", 'slow_page.php', false);
AJAX.send(null);
}
else
{
alert('something is wrong with AJAX!');
}
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
Slow_page.php :
<?
usleep(100000);//sleep for 1/10th of a second, to allow iFrame time to load without DOSing our own server!
?>
我确实想指出,我说过除了该函数之外没有什么可以更改的,并且添加 php 页面确实违反了该“规则”,但在可能的情况下我能够做到这一点。如果我无法做到这一点,我可以调用blank_frame.html而不是slow_page.php,并且它应该只需要调用它一次(因此每帧加载两次)假设它的响应时间与 iFrame 加载的时间相同。如果由于某种原因 iFrame 加载速度较慢,它可能会调用它 2ce(总共 3 次调用服务器)
There is a JavaScript function, of which I have zero control of the code, which calls a function that I wrote. My function uses DOM to generate an iFrame, defines it's src and then appends it to another DOM element. However, before my function returns, and thus allows continued execution of the containing function, it is imperative that the iFrame be fully loaded.
Here are the things that I have tried and why they do not work :
1. The SetTimeout option :
99.999% of the time, this is THE answer. As a matter of fact, in the past decade that I have been mentoring in JavaScript, I have always insisted that code could always be refactored to use this option, and never believed a scenario existed where that was not the case. Well, I finally found one! The problem is that because my function is being called inline, if the very next line is executed before my iFrame finishes loading, it totally neuters my script, and since the moment my script completes, the external script continues. A callback of sorts will not work
2. The "Do nothing" loop :
This option you use while(//iFrame is not loaded){//do nothing}. In theory this would not return until the frame is loaded. The problem is that since this hogs all the resources, the iFrame never loads. This trick, although horribly unprofessional, dirty etc. will work when you just need an inline delay, but since I require an external thread to complete, it will not.
In FF, after a few seconds, it pauses the script and an alert pops up stating that there is an unresponsive script. While that alert is up, the iFrame is able to load, and then my function is able to return, but having the browser frozen for 10 seconds, and then requiring the user to correctly dismiss an error is a no go.
3. The model dialogue :
I was inspired by the fact that the FF popup allowed the iFrame to load while halting the execution of the function, and thinking about it, I realized that it is because the modal dialogue, is a way of halting execution yet allowing other threads to continue! Brilliant, so I decided to try other modal options. Things like alert() work beautifully! When it pops up, even if only up for 1/10th of a second, the iFrame is able to complete, and all works great. And just in case the 1/10 of a second is not sufficient, I can put the model dialogue in the while loop from solution 2, and it would ensure that the iFrame is loaded in time. Sweet right? Except for the fact that I now have to pop up a very unprofessional dialogue for the user to dismiss in order to run my script. I fought with myself about this cost/benefit of this action, but then I encountered a scenario where my code was called 10 times on a single page! Having to dismiss 10 alerts before acessing a page?! That reminds me of the late 90s script kiddie pages, and is NOT an option.
4. A gazillion other delay script out there:
There are about 10 jQuery delay or sleep functions, some of them actually quite cleverly developed, but none worked. A few prototype options, and again, none I found could do it! A dozen or so other libraries and frameworks claimed they had what I needed, but alas they all conspired to give me false hope.
I am convinced that since a built in model dialogue can halt execution, while allowing other threads to continue, there must be some code accessible way to do the same thing with out user input.
The Code is literally thousands upon thousands of lines and is proprietary, so I wrote this little example of the problem for you to work with. It is important to note the ONLY code you are able to change is in the onlyThingYouCanChange function
Test File :
<html>
<head>
</head>
</html>
<body>
<div id='iFrameHolder'></div>
<script type='text/javascript'>
function unChangeableFunction()
{
new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
new_iFrame_doc = (new_iFrame.contentWindow || new_iFrame.contentDocument);
if(new_iFrame_doc.document)new_iFrame_doc=new_iFrame_doc.document;
new_iFrame_body = new_iFrame_doc.body;
if(new_iFrame_body.innerHTML != 'Loaded?')
{
//The world explodes!!!
alert('you just blew up the world! Way to go!');
}
else
{
alert('wow, you did it! Way to go!');
}
}
var iFrameLoaded = false;
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
unChangeableFunction();
</script>
</body>
blank_frame.html :
<html>
<head>
</head>
<body style='margin:0px'>Loaded?</body>
</html>
HERE IS THE ANSWER I MADE FROM COMBINING IDEAS FROM RESPONDERS! YOU GUYS ROCK!
new source of the function I was allowed to change :
function onlyThingYouCanChange(objectToAppendIFrameTo)
{
iFrameLoaded = false;
iframe=document.createElement('iframe');
iframe.onload = new Function('iFrameLoaded = true');
iframe.src = 'blank_frame.html'; //Must use an HTML doc on the server because there is a very specific DOM structure that must be maintained.
objectToAppendIFrameTo.appendChild(iframe);
var it = 0;
while(!iFrameLoaded) //I put the limit on here so you don't
{
if (window.XMLHttpRequest)
{
AJAX=new XMLHttpRequest();
}
else
{
AJAX=new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX)
{
AJAX.open("GET", 'slow_page.php', false);
AJAX.send(null);
}
else
{
alert('something is wrong with AJAX!');
}
//If I was able to put some sort of delay here that paused the exicution of the script, but did not halt all other browser threads, and did not require user interaction we'd be golden!
//alert('test'); //This would work if it did not require user interaction!
}
return iframe;
}
slow_page.php :
<?
usleep(100000);//sleep for 1/10th of a second, to allow iFrame time to load without DOSing our own server!
?>
I do want to note that I stated that there was nothing outside of that function that I could change, and adding the php page did violate that "rule" but in may case I was able to do that. If I were not able to do that, I could have called blank_frame.html instead of slow_page.php, and it should have only ever needed to call it once (so 2 times per frame load) assuming that it responded in an identical amount of time as the iFrame load. If for some reason the iFrame load was slower, it might call it 2ce (a total of 3 calls to the server)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
是的,事实上 javascript 是单线程的,这确实让你很烦恼。您可以使用对故意慢速页面的同步 ajax 调用来模拟睡眠,但您不会获得您想要的结果。为什么不在调用不可更改的函数之前确保 IFrame 已加载?
Yeah, the fact that javascript is single threaded really bites you here. You can use a synchronous ajax call to a purposefully slow page to emulate a sleep, but you aren't going to get the results you want. Why don't you just make sure that your IFrame is loaded before unchangeable function is called?
注意这是非常hacky的,我不会在任何现实情况下使用它。除其他潜在问题外,如果流量足够,您最终可能会遭到 DDOS 攻击。
您可以通过进行非异步 (A)JAX 调用来创建睡眠功能。在某些较旧的浏览器中,这可能会冻结所有内容,但至少不需要任何类型的用户响应。
NB This is extremely hacky, and I wouldn't use it in any real-world situation. Among other potential issues, given sufficient traffic you could end up DDOSing yourself.
You could create sleep functionality by making non-asynchronous (A)JAX calls. In some older browsers this may freeze everything, but at least it won't require any kind of user response.
您真正需要的是在 iFrame 内容加载时触发的事件。这实际上非常简单,因为 iFrame 内的页面有自己的事件,并且可以访问父页面上的脚本。不过,您需要能够更改 iFrame 的内容。
在您的 iFrame 中,您将需要这段代码,
然后在您的父页面中,创建一个名为“myFunction”的函数,并将需要触发的所有脚本放入其中。这应该每次都有效。
编辑:要使其正常工作,您确实需要两个函数。我假设这确实不是一种选择,因此我们将修改一个函数以包含两个函数,并在需要时调用正确的部分。
iFrame 中的脚本现在应该更改为如下所示:
我还没有测试过它,但理论上应该可以做到这一点
What you really need is an event to be fired when the iFrame content has loaded. This is actually really easy because the page inside the iFrame has its own events and it can access scripts on the parent page. You will need to be able to change the contents of the iFrame though.
In your iFrame, you'll need this piece of code
Then in your parent page, make a function called "myFunction" and put all the scripts you need to fire in there. This should work every time.
Edit: To get this to work you really need two functions. I'm assuming that's really not an option so we'll hack the one function to contain two functions and call the right part when we need it to.
The script in the iFrame should now be changed to something like this:
I haven't tested it, but in theory that should do it
使用 XPCOM 的一个令人惊讶的简单 ;-} 答案:
适用于最新版本的 Firefox。抱歉,探索者没有希望了!
A stupefyingly simple ;-} answer using XPCOM:
Works in recent version of Firefox. Sorry no hope for Explorer!
在这种情况下,递归函数可能会有所帮助。只需调用该函数,直到全局变量指示框架已加载
A recursive function might help out in this case. just call the function until a global variable indicates that the frame is loaded
为什么不能修改基础代码? 可能相当简单
例如,将核心函数从 更改为如下所示
: 如果这对您不起作用,那么对原始代码进行透明修改怎么样? 使用 Javascript Strands 并使用内置 futures支持处理这个问题。请注意,Javascript 1.7 还支持延续,但需要手动更改代码才能使用它们。
Why can you not modify the base code? For example, it could be fairly simple to change the core function from
To something like this:
If that doesn't work for you, how about a transparent modification of the original code? Compile it with Javascript Strands and use the built-in futures support to handle this. Note that Javascript 1.7 also supports continuations, but would require changing the code manually to use them.
另一种解决方案可能不适用,具体取决于您对原始代码的简化程度。您可以设置一个 onload 处理程序,然后抛出一个错误,然后在 onload 处理程序中调用
unChangeableFunction
:此函数(如
unChangeableFunction
)将被调用两次:第一次调用一次,然后当触发onload
处理程序时再次执行。两种不同的途径反映了这一点。再说一次,这是很hacky的,并且明显滥用了JS的错误功能。
Another solution that may not be applicable, depending on how much you have simplified the original code. You could set an onload handler, then throw an error, then call
unChangeableFunction
in your onload handler:This function (like
unChangeableFunction
) will be called twice: once in the first instance, then again when theonload
handler is triggered. The two different pathways reflect this.Again, this is hacky, and a definite abuse of JS's error functionality.
您可以像这样使用cookie和setTimeout:
在blank_frame.html中添加一个脚本:
基本上您正在添加一个值为
yes
的cookieiframe_loaded
。IMO 最好删除 cookie,因为如果您要重新加载页面,则需要执行相同的操作。
您也可以在 setCookie 函数调用中设置域。
现在在主文件中,我们将使用 setTimeout 和函数来检查 cookie 是否存在,如果存在,则该函数将返回 iframe,就像您的代码中一样:
因为在此文件中也删除了故障安全 cookie。
希望这会给你带来一些帮助:)
干杯
G.
you can use cookie and setTimeout like that:
in blank_frame.html add a script:
Basically you're adding a cookie
iframe_loaded
with valueyes
.IMO it's better to remove the cookie as you need to do the same if you'll reload the page.
You can as well set the domain in setCookie function call.
Now in main file we'll use setTimeout with function that will check if the cookie exists, if it does then the function will return iframe like in your code:
As a failsafe cookie is being deleted in this file as well.
Hopefully that will give you something to work with :)
Cheers
G.