2010-05-10 38 views
8

On my web site, estoy tratando de lograr la carga de página más rápida posible.JavaScript: ¿Cómo descargar JS de forma asíncrona?

Me he dado cuenta de que parece que mi JavaScript no se está cargando de forma asincrónica. Imagen vinculada a continuación.

alt text http://img249.imageshack.us/img249/2452/jsasynch2.png

¿Cómo funciona mi sitio web es que se necesita para cargar dos archivos externos de JavaScript:

  • Google Maps v3 JavaScript y
  • jQuery JavaScript

Entonces , Tengo JavaScript en línea dentro del HTML que no se puede ejecutar hasta que se descarguen los dos archivos anteriores.

Una vez que carga estos archivos javascript externos, entonces, y solo entonces, puede representar dinámicamente la página. La razón por la cual mi página no se puede cargar hasta que se carguen Google Maps y JQuery es que mi página, basada en la geolocalización (usando Gmaps) del usuario, mostrará la página en función de dónde se encuentran (por ejemplo, Nueva York, San Francisco, etc.) Es decir, dos personas en diferentes ciudades que miran mi sitio verán diferentes páginas iniciales.

Pregunta: ¿Cómo puedo obtener mis archivos JavaScript para que se descarguen de forma asíncrona, de modo que el tiempo total de carga de la página sea el más rápido?

ACTUALIZACIÓN:

Si tuviera que descargar, de alguna manera, Google-maps y jQuery forma asíncrona, ¿cómo iba a crear un evento que se disparó una vez ambos Google mapas y jQuery han descargado desde mi la página tiene una fuerte dependencia de esos archivos para ejecutar.

ACTUALIZACIÓN 2

A pesar de que hay 3 respuestas a continuación, ninguno en realidad todavía responder el problema que tengo. Cualquier ayuda sería muy apreciada.

+0

debe mover todo el JavaScript en línea a javascript no objeción y eso solucionará algunos de sus problemas. –

+0

Con respecto a su actualización, solo cree una función que se llama cuando * cada * de sus scripts está cargado, y haga que llame a su otro código en la * segunda * hora a la que se llama. Es decir. la primera vez que se invoca, establece una variable y la segunda vez (cuando detecta que la variable está configurada), ejecuta tu código. – tloflin

+0

@tloflin, lo que acabas de describir es crear un "efecto de cascada", que no es ** asynch **. – Teddyk

Respuesta

7

descargas HTTP se limitan generalmente por los navegadores a dos descargas simultáneas por dominio . Esta es la razón por la cual algunos sitios tienen el contenido dinámico en www.domain.tla y the images and javascript on static.domain.tla.

Pero navegadores actúan de forma ligeramente diferente con guiones, while a script is downloading, however, the browser won't start any other downloads, even on different hostnames.

La solución estándar es mover secuencias de comandos para la parte inferior de la página, pero hay una solución que podría o no podría funcionar para usted: Insert the script DOM element using Javascript.

+0

Eso también fue lo primero que pensé, pero los dos scripts * en realidad * se originan en diferentes dominios. Después de todo, solo diría: es un comportamiento específico del navegador. En Firefox, las descargas por dominio son, por cierto, configurables como 'network.http.max-connections' en' about: config'. Por defecto es 15. – BalusC

+1

@BalusC: la descarga de un archivo javascript bloquea todas las demás descargas, independientemente de los dominios. Además, los valores predeterminados están demasiado arraigados para que los usuarios los cambien;) – voyager

+0

Y el creador de YSlow, al que también vinculó, afirma que ** puede ** realizar descargas de asynch JS - http://www.stevesouders.com/blog/2009/04/27/loading-scripts-without-blocking/Sin embargo, dado mi requisito de que tanto Gmaps como JQuery se deben descargar primero, no estoy seguro de cómo usar el ejemplo de Steve. * Como tal, no creo que tu respuesta sea correcta. * – Teddyk

1

Sin importar qué orden descarga en los guiones deben ser analizados / ejecutados en el orden en que se presentan en la página (a menos que utilice DEFER).

Así que puedes poner primero Google Maps en la cabeza, ENTONCES JQuery.Luego, en el cuerpo de su página en alguna parte:

<script language="Javascript"> 

    function InitPage() { 
     // Do stuff that relies on JQuery and Google, since this script should 
     // not execute until both have already loaded. 
     } 

    $(InitPage); // this won't execute until JQuery is ready 

</script> 

Pero esto tiene la desventaja de bloquear sus otras conexiones mientras se carga el comienzo de la página, que no es tan impresionante para un rendimiento página.

su lugar, puede mantener jQuery en la cabeza, pero cargar las secuencias de comandos de Google de la función InitPage(), usando Javascript La funcionalidad de carga de jQuery en lugar de la JSAPI Google. A continuación, inicie su representación cuando se ejecute esa función de devolución de llamada. Igual que el anterior, pero con esta función InitPage() en su lugar:

function InitPage() { 
    $.getScript('Google Maps Javascript URL', function() { 
     // Safe to start rendering now 
     }); 
+0

¿Esto todavía sería asych? Dado que parece que no comienza a descargar Google Maps hasta que JQuery descargue primero. – Teddyk

+0

Cierto, pero JQuery es una carga rápida. La solución anterior resuelve el problema de dependencia, no las conexiones de red asincrónicas. Puede usar el documento.write hack en la parte superior para iniciar la carga asincrónica de JQuery y Google, y pruebe a utilizar DEFER en su código de página principal, pero eso no garantizará que su script principal espere en los otros scripts. Para hacerlo, necesitará su propia función "lista" que pruebe los objetos de JQuery y Google Maps, espere unos milisegundos e intente nuevamente si no están disponibles, y agote el tiempo con un error apropiado si no lo hacen t carga en un período de tiempo razonable. – richardtallent

4

Usted podría utilizar algo como esto, que funciona bastante bien en la mayoría de los navegadores. Tiene algunos problemas en IE6 al menos, pero realmente no tengo tiempo para investigarlos.

var require = function (scripts, loadCallback) { 
    var length  = scripts.length; 
    var first   = document.getElementsByTagName("script")[0]; 
    var parentNode = first.parentNode; 
    var loadedScripts = 0; 
    var script; 

    for (var i=0; i<length; i++) { 
     script = document.createElement("script"); 
     script.async = true; 
     script.type = "text/javascript"; 
     script.src = scripts[i]; 

     script.onload = function() { 
      loadedScripts++; 

      if (loadedScripts === length) { 
       loadCallback(); 
      } 
     }; 

     script.onreadystatechange = function() { 
      if (script.readyState === "complete") { 
       loadedScripts++; 

       if (loadedScripts === length) { 
        loadCallback(); 
       } 
      } 
     }; 

     parentNode.insertBefore(script, first); 
    } 
}; 


require([ 
    "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js", 
    "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js", 
    "http://ajax.googleapis.com/ajax/libs/yui/2.7.0/build/yuiloader/yuiloader-min.js" 
], function() { 
    console.log(jQuery); 
    console.log($); 
    console.log(YAHOO); 
}); 
+0

Pero, ¿cuál sería mi función de devolución de llamada si tengo que esperar hasta que se hayan descargado 2 archivos JS? – Teddyk

+0

La devolución de llamada se desencadena después de que se hayan cargado todas las URL de la matriz. Si coloca dos URL en esa matriz, la devolución de llamada se activará después de que ambas se hayan cargado. Puede haber casos más complejos, pero no pretendo haber ofrecido una solución exhaustiva. Es simplemente un punto de partida para sus necesidades. –

4

alguien me preguntó a comentar en este hilo, pero eso fue antes de @lonut registró una respuesta. El código de @ lonut es una muy buena solución, pero tengo algunos comentarios (críticos y no tan críticos):

Primero, el código de @ lonut asume que los scripts NO tienen dependencias de carga en los otros scripts. Esto es un poco difícil de explicar, así que trabajemos con el ejemplo simple de jquery.min.js y prototype.js. Supongamos que tenemos una página simple que acaba de carga estas dos guiones como este:

<script src="jquery.min.js"></script> 
<script src="prototype.js"></script> 

Recuerde - no hay nada más en la página - ningún otro código JavaScript. Si carga esa página, los dos scripts se descargan y todo está bien. Ahora, ¿qué sucede si elimina el script jquery.min.js? Si obtiene errores de prototype.js porque está tratando de hacer referencia a los símbolos definidos en jquery.min.js, entonces prototype.js tiene una dependencia de carga en jquery.min.js; no puede cargar prototype.js a menos que jquery.min.js tenga ya ha sido cargado Sin embargo, si no obtiene ningún error, los dos scripts se pueden cargar en el orden que desee. Suponiendo que no tiene dependencias de carga entre sus scripts externos, el código de @ lonut es genial. Si tiene dependencias de carga, se vuelve muy difícil y debe leer el Capítulo 4 en Sitios web aún más rápidos.

En segundo lugar, un problema con el código de @ lonut es que algunas versiones de Opera llamarán a loadCallback dos veces (una desde el controlador de onload y una segunda desde el controlador onreadystatechange). Simplemente agregue una bandera para asegurarse de que loadCallback solo se llame una vez.

En tercer lugar, la mayoría de los navegadores actuales abren más de 2 conexiones por nombre de host. Ver Roundup on Parallel Connections.

+0

Sounders, estoy ** extremadamente ** honrado de que hagas un comentario sobre mi publicación. Dicho esto, ¿hay alguna solución a mi pregunta? En caso de que ayude, he publicado una segunda pregunta que está directamente relacionada con este problema en http://stackoverflow.com/questions/2804494/javascript-why-does-my-asrnchronous-download-not-download-asynchronously que podría proporcionarte más detalles. De nuevo, muchas gracias y abrumado por que estás en StackOverlow :) – Teddyk

+0

De hecho, he ignorado intencionalmente el primer problema, ya que en mi opinión puede resolverse emitiendo otra llamada a 'require' dentro de la devolución de llamada.No he hecho ninguna prueba, pero parece que debería funcionar. En cuanto al segundo problema, sabía que había algunos navegadores en los que la devolución de llamada se activaría dos veces, pero no podría desencadenar el error ya que tengo la última versión de Opera (y IE6 funcionó bien). ¡De todos modos, gracias por los comentarios! –

2

El cargador de script dinámico LABjs está diseñado específicamente para este tipo de caso. Por ejemplo, es posible hacer:

$LAB 
    .script("googlemaps.js") 
    .script("jquery.js") 
    .wait(function(){ 
    // yay, both googlemaps and jquery have been loaded, so do something! 
    }); 

Si la situación es un poco más compleja, y ha tenido algunos scripts que tenían dependencias entre sí, como Steve Souders ha mencionado, entonces es posible hacer:

$LAB 
    .script("jquery.js") 
    .wait() // make sure jquery is executed first 
    .script("plugin.jquery.js") 
    .script("googlemaps.js") 
    .wait(function(){ 
    // all scripts are ready to go! 
    }); 

En cualquier caso, LABjs descargar todos los scripts ("jquery.js", "googlemaps.js" y "plugin.jquery".js ") en paralelo, como mínimo hasta el punto que permita el navegador. Pero con el uso juicioso de .wait() en la cadena, LABjs se asegurará de que se ejecuten en el orden correcto. Es decir, si no hay .wait() entre los dos scripts en la cadena, cada uno ejecutará ASAP (es decir, orden indeterminado entre tehm). Si hay un .wait() entre dos scripts en la cadena, entonces el primer script hará ejecutar antes el segundo guión, a pesar de que cargan en paralelo.

1

Mueva su javascript incluye (<script src="...) del elemento HEAD hasta el final de su elemento BODY. en general todo lo que se coloca en la cabeza está cargado de forma sincrónica, lo que se coloca en el BODY es cargado asincrónicamente Esto es más o menos cierto para las secuencias de comandos incluidas, sin embargo, la mayoría de los navegadores actualmente bloquean todo lo que está debajo del script hasta que se carga, por lo que la mejor práctica es incluir scripts en la parte inferior del cuerpo.

Aquí es el guildline YUI para este que explica en más detalle: http://developer.yahoo.net/blog/archives/2007/07/high_performanc_5.html

Ésta es también la razón por la cual las hojas de estilo deben estar en la cabeza, y Javascript debe estar en el cuerpo. Como no queremos ver nuestra página pasar de espaguetis a amabilidad, mientras que los estilos se cargan de manera asincrónica, y no queremos esperar en nuestro javascript mientras carga nuestra página.

2

Aquí es cómo me las he arreglado para cargar gmaps de forma asíncrona en un móvil jQuery: En primer lugar, puede cargar jQuery (es decir, con la requieren función publicado anteriormente por Ionuţ G. Stan) A continuación, puede hacer uso de la devolución de llamada parámetro en gmaps para hacer lo siguiente:

<!DOCTYPE html> 
<html> 
<body> 
<script type="text/javascript"> 
     var require = function (scripts, loadCallback) { 
     var length  = scripts.length; 
     var first   = document.getElementsByTagName("script")[0]; 
     var parentNode = first.parentNode; 
     var loadedScripts = 0; 
     var script; 

     for (var i=0; i<length; i++) { 
      script = document.createElement("script"); 
      script.async = true; 
      script.type = "text/javascript"; 
      script.src = scripts[i]; 

      script.onload = function() { 
       loadedScripts++; 

       if (loadedScripts === length) { 
        loadCallback(); 
       } 
      }; 

      script.onreadystatechange = function() { 
       if (script.readyState === "complete") { 
        loadedScripts++; 

        if (loadedScripts === length) { 
         loadCallback(); 
        } 
       } 
      }; 
      parentNode.insertBefore(script, first); 
     } 
     }; 

     require([ 
     "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js",], function() { 

     $.ajax({ 
      type: "GET", 
      url: 'http://maps.googleapis.com/maps/api/js?v=3&sensor=false&callback=setMyMap', 
      dataType: "script" 
     }); 

     }); 

     function setMyMap() { 


         console.log('your actions here'); 



      var coords = new google.maps.LatLng(40.5439532,-3.6441775); 
      var mOptions = { 
       zoom: 8, 
       center: coords, 
       mapTypeId: google.maps.MapTypeId.ROADMAP 
      } 
      var map = new google.maps.Map(document.getElementById("gmap"), mOptions); 
     } 
</script> 

<div id="gmap" style="width:299px; height:299px"></div> 

</body> 

el punto es asíncrono jQuery carga (whathever método que elija) y en ese lugar de devolución de llamada de una nueva llamada asincrónica a gmaps con su st método artístico en el callback parámetro de la secuencia de comandos gmaps.

creo que sirve

0

El objetivo que tiene en mente serían atendidos por el uso de RequireJS. RequireJS descarga los recursos js de forma asíncrona. Es una biblioteca muy simple y útil de implementar. Por favor, lea más aquí. http://requirejs.org/

Cuestiones relacionadas