不可能的内联Javascript延迟/睡眠

时间:2011-01-01 23:57:03

标签: javascript delayed-execution

有一个JavaScript函数,我对代码没有控制权,它调用了我编写的函数。我的函数使用DOM生成一个iFrame,定义它的src,然后将它附加到另一个DOM元素。但是,在我的函数返回之前,因此允许继续执行包含函数,必须完全加载iFrame。

以下是我尝试的内容以及它们无法正常工作的原因:

1。 SetTimeout选项:
99.999%的时间,这是答案。事实上,在过去十年中我一直在使用JavaScript进行指导,我一直坚持认为代码总是可以重构以使用这个选项,并且从不相信存在情况并非存在的情况。好吧,我终于找到了一个!问题是因为我的函数被内联调用,如果在我的iFrame完成加载之前执行下一行,那么我的脚本完全是neuters,并且从脚本完成的那一刻开始,外部脚本继续。各种回调都不起作用

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>


这是我从响应者那里结合思想而来的答案!你好摇滚!
我被允许改变的新功能来源:

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,它应该只需要调用一次(因此每帧加载2次),假设它在与iFrame加载相同的时间内响应。如果由于某种原因iFrame负载较慢,它可能会将其称为2ce(总共3次调用服务器)

8 个答案:

答案 0 :(得分:3)

是的,javascript是单线程的事实真的在这里咬你。您可以对有目的的慢页面使用同步ajax调用来模拟睡眠,但是您无法获得所需的结果。为什么不在调用不可更改的函数之前确保加载IFrame?

答案 1 :(得分:2)

您真正需要的是在加载iFrame内容时要触发的事件。这实际上非常简单,因为iFrame中的页面有自己的事件,它可以访问父页面上的脚本。您需要能够更改iFrame的内容。

在您的iFrame中,您需要这段代码

// Use whichever DOMReady function you like, or window.onload would work
window.addEventListener('DOMContentLoaded', function() {
    if (parent.window.myFunction) {
        parent.window.myFunction();
    }
}, false);

然后在您的父页面中,创建一个名为“myFunction”的函数,并将所需的所有脚本放在那里。这应该每次都有效。

编辑:要实现此功能,您需要两个功能。我假设这不是一个选项,所以我们将破解一个函数来包含两个函数,并在需要时调用正确的部分。

function onlyThingYouCanChange(stringOrObject) {
    function createIFrame(objectToAppendIFrameTo) {
        // This comment represents all the code that appends your iFrame
    }
    function onIFrameReady() {
        // This comment represents all the stuff you want to happen when the iFrame is ready
    }

    // The bones of it
    if (stringOrObject === "iFrameLoaded") {
        onIFrameReady();
    } else {
        createIFrame(stringOrObject);
    }
}

iFrame中的脚本现在应该更改为:

// Use whichever DOMReady function you like, or window.onload would work
window.addEventListener('DOMContentLoaded', function() {
    if (parent.window.onlyThingYouCanChange) {
        parent.window.onlyThingYouCanChange('iFrameLoaded');
    }
}, false);

我没有测试过,但理论上应该这样做

答案 2 :(得分:2)

NB 这非常hacky,我不会在任何实际情况下使用它。在其他潜在的问题中,如果有足够的流量,你最终可能会自己DDOS。

您可以通过进行非异步(A)JAX调用来创建睡眠功能。在一些较旧的浏览器中,这可能会冻结所有内容,但至少它不需要任何类型的用户响应。

while (!iFrameLoaded)
{
    if (XMLHTTPRequest) {
        var request = new XMLHttpRequest();
    } else {
        var request = new ActiveXObject("Microsoft.XMLHTTP");
    }

    request.open('GET', 'anyoldfile.htm', false);
    request.send();

    // check if the iframe is loaded and set iFrameLoaded
}

答案 3 :(得分:1)

令人难以置信的简单; - }使用XPCOM答案:

// Get instance of the XPCOM thread manager.
var threadManager=Components.classes['@mozilla.org/thread-manager;1'].getService(
                      Components.interfaces.nsIThreadManager);
// Release current thread.
function doThread() {threadManager.currentThread.processNextEvent(false);};

// Event enabled delay, time in ms.
function delay(time) {
  var end;
  var start=Date.now();
  do {
    end=Date.now();
    doThread();
  } while ((end-start) <= time);
}

适用于最新版本的Firefox。抱歉没有希望的资源管理器!

答案 4 :(得分:1)

在这种情况下,递归函数可能会有所帮助。只需调用该函数,直到全局变量指示框架已加载

var iFrameStarted = false; //you need two global vars
var iFrameLoaded  = false;


function onlyThingYouCanChange(objectToAppendIFrameTo)
{
  if (iFrameLoaded=false)  // if the frame has loaded then you are done. skip everything and return iframe
  { if (iFrameStarted = false) //otherwise start the frame if it has not been
  {
   iFrameStarted = true;
   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
   objectToAppendIFrameTo.appendChild(iframe);
   var it = 0;
   for (i=0;i<10000;i++) {}  //slow down execution so you are not recursing yourself to death
   onlyThingYouCanChange(objectToAppendIFrameTo);   //start the recursion process

  }
  else  //the frame has been started so continue recursion until the frame loaded
  {
   for (i=0;i<10000;i++) {}  //slow down execution so you are not recursing yourself to death
   onlyThingYouCanChange(objectToAppendIFrameTo);   recursively call your function until the frame is loaded

  }

}

return iframe;  //you only get here when all the recursions are finished   
}

答案 5 :(得分:0)

为什么不修改基本代码?例如,从

更改核心功能可能相当简单
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!');
    }
}

对于这样的事情:

function unChangeableFunction()
{
    var new_iFrame = onlyThingYouCanChange(document.getElementById('iFrameHolder'));
    new_iFrame.onload = function()
    {
        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!');
        }
    };
}

如果这对您不起作用,原始代码的透明修改怎么样?使用Javascript Strands进行编译并使用内置的期货支持来处理此问题。请注意,Javascript 1.7还支持continuation,但需要手动更改代码才能使用它们。

答案 6 :(得分:0)

另一种可能不适用的解决方案,具体取决于您对原始代码的简化程度。你可以设置一个onload处理程序,然后抛出一个错误,然后在你的onload处理程序中调用unChangeableFunction

function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    // using global variable func_called
    if (!func_called) {
        func_called = true;
        var iframe=document.createElement('iframe');
        iframe.src = 'blank_frame.html';
        iframe.id = 'myIframe';
        iframe.onload = function() {
            unChangeableFunction();
        };
        objectToAppendIFrameTo.appendChild(iframe);

        throw new Error('not an error');
    } else {
        return document.getElementById('myIframe');
    }
}

此函数(如unChangeableFunction)将被调用两次:在第一个实例中调用一次,然后在触发onload处理程序时再次调用。两种不同的途径反映了这一点。

同样,这是hacky,并且明确滥用了JS的错误功能。

答案 7 :(得分:0)

你可以像这样使用cookie和setTimeout:

<_>在blank_frame.html中添加一个脚本:

<script type="text/javascript">
function deleteCookie(cookie_name)
{
  var cookie_date=new Date();
  cookie_date.setTime(cookie_date.getTime()-1);
  document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
}
function setCookie(name,value,expires,path,domain,secure){
    document.cookie=name+"="+escape(value)+((expires)?"; expires="+expires.toGMTString():"")+((path)?"; path="+path:"")+((domain)?"; domain="+domain:"")+((secure)?"; secure":"");
}

window.onload=function(){
    setCookie('iframe_loaded','yes',false,'/',false,false);
}
</script>

基本上,您要添加值为iframe_loaded的Cookie yes。 如果您要重新加载页面,IMO最好删除cookie,因为您需要这样做。 您也可以在setCookie函数调用中设置域。

现在在主文件中我们将使用setTimeout和函数来检查cookie是否存在,如果确实存在,那么函数将返回iframe中的iframe:

function onlyThingYouCanChange(objectToAppendIFrameTo)
{
    function get_cookie(cookie_name){
        var results = document.cookie.match('(^|;) ?'+cookie_name+'=([^;]*)(;|$)');
        return results?unescape(results[2]):null;
    }
    function deleteCookie(cookie_name){
        var cookie_date=new Date();
        cookie_date.setTime(cookie_date.getTime()-1);
        document.cookie=cookie_name+="=;expires="+cookie_date.toGMTString();
    }

    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;

    function checkiframe(){
        if(get_cookie('iframe_loaded')=="yes"){
            alert('iframe loaded');
            deleteCookie('iframe_loaded');
            return iframe;
        }else{
            setTimeout(checkiframe,1000);
        }
    }

    checkiframe();
}

由于此文件中的故障安全cookie也被删除。 希望这会给你一些工作:)

干杯

-G。