2011-01-01 7 views
8

Hay una función de JavaScript, de los cuales tengo el control cero del código, que llama a una función que escribí. Mi función usa DOM para generar un iFrame, define su src y luego lo agrega a otro elemento DOM. Sin embargo, antes de que mi función regrese, y así permita la ejecución continua de la función contenedora, es imperativo que el iFrame esté completamente cargado.La línea de retardo imposible Javascript/sueño

Estas son las cosas que he tratado y por qué no funcionan:

1. La opción SetTimeout:
99,999% de las veces, esta es la respuesta. Como cuestión de hecho, en los últimos diez años que he estado Mentoring en JavaScript, siempre he insistido en que el código siempre podría ser rediseñado para utilizar esta opción, y nunca se cree existió un escenario en que no era el caso. Bueno, ¡finalmente encontré uno! El problema es que debido a mi función está siendo llamado en línea, si se ejecuta la siguiente línea antes de mi iFrame termina de cargar, se neutraliza totalmente mi escritura, y desde el momento en que mi escritura termina, el guión sigue externa. Una devolución de llamada de tipo no funcionará

2. El bucle "No hacer nada":
Esta opción se utiliza, mientras que (// iFrame no está cargado) {// hacer nada}. En teoría, esto no volvería hasta que se cargue el marco. El problema es que, dado que esto encierra todos los recursos, el iFrame nunca se carga. Este truco, aunque terriblemente poco profesional, sucio, etc. funcionará cuando solo necesite un retraso en línea, pero como necesito un hilo externo para completar, no lo hará.
En FF, después de unos segundos, pausa la secuencia de comandos y aparece una alerta que indica que hay una secuencia de comandos que no responde. Mientras la alerta está activa, iFrame puede cargar y luego mi función puede regresar, pero tener el navegador congelado durante 10 segundos y luego requerir que el usuario descarte un error correctamente es no ir.

3. El modelo de diálogo:
Me inspiré en el hecho de que la ventana emergente FF permitió que el marco flotante para cargar mientras que la detención de la ejecución de la función, y pensar en ello, me di cuenta de que es debido a que el modal diálogo, ¡es una forma de detener la ejecución permitiendo que otros hilos continúen! Brillante, así que decidí probar otras opciones modales. Cosas como alert() funcionan muy bien! Cuando aparece, aunque solo sea durante una décima de segundo, el iFrame puede completarse y todo funciona muy bien. Y en caso de que el 1/10 de segundo no sea suficiente, puedo poner el diálogo de modelo en el ciclo while de la solución 2, y garantizaría que el iFrame se cargue a tiempo. Dulce ¿verdad? Excepto por el hecho de que ahora tengo que abrir un diálogo muy poco profesional para que el usuario descarte para ejecutar mi script. Luché conmigo mismo sobre este costo/beneficio de esta acción, pero luego me encontré con un escenario en el que mi código se llamaba 10 veces en una sola página. ¡Tener que descartar 10 alertas antes de acceder a una página! Eso me recuerda a las páginas de guiones de finales de los 90, y NO es una opción.

4. Un otro script retraso tropecientos por ahí:
Hay alrededor de 10 o retardo del sueño funciones de jQuery, algunos de ellos en realidad muy hábilmente desarrollados, pero ninguno funcionó. Algunas opciones de prototipos, y nuevamente, ¡ninguna que encontré podría hacerlo! Una docena más o menos de otras bibliotecas y marcos afirmaron que tenían lo que necesitaba, pero, por desgracia, todos conspiraron para darme falsas esperanzas.

estoy convencido de que ya construido en un modelo de diálogo puede detener la ejecución, al tiempo que permite a otros hilos siguen, tiene que haber alguna manera accesible código para hacer lo mismo con la entrada del usuario a cabo.

El Código es literalmente miles y miles de líneas y es propietaria, así que escribí este pequeño ejemplo del problema para que usted pueda trabajar con ellos.Es importante tener en cuenta el código sólo es capaz de cambiar está en la función onlyThingYouCanChange

archivo de prueba:

<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> 


Aquí está la respuesta que ¡HECHO DE COMBINAR IDEAS DE RESPONDEDORES! ¡USTEDES MOLAN!
nueva fuente de la función que se le permitió cambiar:

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! 
?> 

sí quiero señalar que he dicho que no había nada fuera de esa función que me podría cambiar, y agregar la página php sí violaba esa "regla", pero en el caso de que fuera capaz de hacerlo. Si no fuera capaz de hacerlo, I podría haber llamado blank_frame.html en lugar de slow_page.php, y solo debería haber sido necesario llamarlo una vez (por lo que 2 veces por carga de cuadro) suponiendo que respondiera de forma idéntica cantidad de tiempo que carga el iFrame. Si por alguna razón la carga de iFrame fuera más lenta, podría llamarla 2ce (un total de 3 llamadas al servidor)

+1

No se puede combinar 'new Function() {return code; } 'con la devolución de llamada de' setTimeout'? –

+2

Personalmente, creo que cualquier diseño que requiera que este sea el caso es altamente defectuoso, pero desde una perspectiva académica me interesaría ver si hay una respuesta. –

+0

meder, setTimeout permitirá la ejecución continua de Javascript, a menos que tenga una forma de combinar aquellos que no conozco. Mencioné algunos complementos de jQuery que hicieron algunas cosas muy ingeniosas, con eso, pero analicé todo lo que pude encontrar y ninguno funcionó para mí. Si puede encontrar la manera de hacerlo funcionar en el ejemplo anterior, ¡estaría encantado! Brian. Estoy de acuerdo con usted 99.999%, y hace unos días habría dicho 100%, y habría discutido con cualquiera que dijera que era necesaria una demora en línea. Desafortunadamente no tengo el control para arreglarlo aquí! – trex005

Respuesta

1

NB Esto es extremadamente hacky, y no lo usaría en ninguna situación del mundo real. Entre otros problemas potenciales, dado el tráfico suficiente, podrías terminar drogándote.

Puede crear la funcionalidad de reposo realizando llamadas JAX no asincrónicas (A). En algunos navegadores más antiguos esto puede congelar todo, pero al menos no requerirá ningún tipo de respuesta del usuario.

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 
} 
+0

Podría poner 100 URL en diferentes servidores si fuera necesario (google, amazon, yahoo, whtismyip, etc.) para las llamadas ajax que podrían pasar ... Simplemente descartaría los resultados de todos modos ... estoy voy a jugar con esta idea por un tiempo, supongo que de 1-2 llamadas sería un gran retraso de todos modos. ¡Te dejaré saber cómo va! – trex005

+3

@ trex005 Dada la política de origen idéntico, necesitará las URL en su propio dominio, de lo contrario, las solicitudes fallarán instantáneamente. – lonesomeday

2

Lo que realmente necesita es un evento que se dispare cuando se haya cargado el contenido de iFrame. Esto es realmente muy fácil porque la página dentro del iFrame tiene sus propios eventos y puede acceder a los scripts en la página principal. Sin embargo, deberá poder cambiar el contenido del iFrame.

En el marco flotante, necesitará este pedazo de código

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

Luego, en su página principal, hacer una función llamada "myFunction" y poner todas las secuencias de comandos que necesita para disparar en ese país. Esto debería funcionar todo el tiempo.

Edit: Para que esto funcione, realmente necesita dos funciones. Supongo que realmente no es una opción, por lo que hackeará una función para contener dos funciones y llamar a la parte correcta cuando lo necesitemos.

 
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); 
    } 
} 

La secuencia de comandos en el marco flotante ahora se debe cambiar a algo como esto:

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

yo no lo he probado, pero en la teoría de que debe hacerlo

+0

Hay algunos problemas con esto que cubrí en mi pregunta. 1) No puedo cambiar nada fuera de esa función que especifiqué. Las razones detrás de esto son complejas, así que voy a tener que confiar en mí en este 2) Si miras mi código, verás que ya llamo una función cuando iFrame termina de cargar. Esa es la parte simple. El problema es que tengo que retrasar el resto de la ejecución del código posterior hasta que se active ese evento, lo que no veo en absoluto cómo lo hace su solución. – trex005

+0

Si hay alguna forma en que pueda hacer que su solución funcione con mi muestra, incluso si tiene que cambiar el marco en blanco.html, también podría lograrlo de una manera creativa. – trex005

+0

OK, edité esta publicación. ¿Eso ayuda en absoluto? –

0

¿Por qué no puede usted modificar el código base? Por ejemplo, podría ser bastante simple de cambiar la función básica de

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!'); 
    } 
} 

Para algo como esto:

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!'); 
     } 
    }; 
} 

Si eso no funciona para usted, ¿qué hay de una modificación de la transparencia código original? Compilarlo con Javascript Strands y usar el soporte de futuros incorporado para manejar esto. Tenga en cuenta que Javascript 1.7 también admite continuaciones, pero requeriría cambiar el código manualmente para usarlas.

3

Sí, el hecho de que javascript es de un solo hilo realmente te muerde aquí. Puede utilizar una llamada sjax síncrona a una página deliberadamente lenta para emular un sueño, pero no obtendrá los resultados que desea. ¿Por qué no solo te aseguras de que tu IFrame esté cargado antes de llamar a la función inmutable?

+0

función inmutable llama a la única función que tengo control, por lo que no tendría ninguna manera de introducir una espera antes de eso. Estoy explorando el Ajax síncrono sin embargo. – trex005

+0

Sí, si haces eso, no necesitas golpear la página cientos de veces, todo lo que tienes que hacer es dormir en tu php/jsp, o en cualquier otra cosa que uses. Agregue un sueño de 100ms, y cargarlo 10 veces le dará un sueño de 1 segundo. – Zeki

+0

Me diste una clave muy importante para resolver este problema (el script PHP usleep), pero solo podía aceptar una respuesta. Ojalá pudiera haber aceptado ambos. ¡Gracias! – trex005

0

Otra solución que puede no ser aplicable, dependiendo de cuánto haya simplificado el código original. También podemos establecer un controlador onLoad, luego tirar un error, a continuación, llamar unChangeableFunction en su controlador onload:

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'); 
    } 
} 

Esta función (como unChangeableFunction) será llamado dos veces: una vez en el primer caso, y de nuevo cuando el manejador onload se dispara. Las dos vías diferentes reflejan esto.

Nuevamente, esto es raro y un abuso definitivo de la funcionalidad de error de JS.

0

puede utilizar cookies y setTimeout así:

en blank_frame.html agregar una secuencia de comandos:

<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> 

Básicamente está añadiendo una cookie con valor iframe_loadedyes. IMO es mejor eliminar la cookie ya que debe hacer lo mismo si va a volver a cargar la página. También puede configurar el dominio en la llamada a la función setCookie.

Ahora en el archivo principal que usaremos setTimeout con la función que va a comprobar si existe la cookie, si lo hace, entonces la función devolverá iframe como en el código:

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(); 
} 

Como está siendo una cookie a prueba de fallos eliminado en este archivo también. suerte que le dará algo para trabajar con :)

Saludos

G.

1

Una sencilla pasmosamente; -} respuesta usando 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); 
} 

Obras en versión reciente de Firefox . ¡Lo siento, no hay esperanza para Explorer!

1

Una función recursiva podría ayudar en este caso. simplemente llame a la función hasta que una variable global indique que el cuadro está cargado

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 
} 
+0

Al echar un vistazo a esto, no veo cómo es diferente de '2. El ciclo "No hacer nada" ¿Puedes dar más detalles? – trex005