2013-11-26 13 views
5

Fornisco un widget JavaScript a diversi siti Web, che caricano in modo asincrono. Il mio widget a sua volta deve caricare uno script fornito da un'altra parte, fuori dal mio controllo.Esegui un pezzo di JavaScript non appena uno script di terze parti non riesce a caricare

Esistono diversi modi per verificare se lo script è stato caricato correttamente. Tuttavia, ho anche bisogno di eseguire un codice diverso se il caricamento di questo script non ha avuto esito positivo.

Gli strumenti ovvio che non funzionano sono:

  • io non sono disposto a utilizzare le librerie JavaScript, come jQuery. Ho bisogno di uno script molto piccolo per ridurre al minimo il mio impatto sui siti che usano il mio widget.

  • Desidero rilevare l'errore il prima possibile, quindi non è consigliabile utilizzare un timer per eseguire il polling. Non mi dispiacerebbe usare un timer come ultima risorsa sui vecchi browser, però.

  • Ho trovato che l'evento <script> del tag onerror non è affidabile in alcuni dei principali browser. (Sembrava dipendere da quali componenti aggiuntivi sono stati installati.)

  • Tutto ciò che riguarda document.write è appena uscito. (Oltre a questo metodo è intrinsecamente cattivi, il mio codice è caricato in modo asincrono in modo document.write può fare cose cattive alla pagina.)

ho avuto un precedente soluzione che ha coinvolto il caricamento del <script> in una nuova . In quell'iframe, ho impostato un gestore di eventi <body onload=...> che controllava se l'evento <script onload=...> era già stato attivato. Poiché <script> era parte del documento iniziale, non iniettato in modo asincrono in seguito, onload viene attivato solo dopo che il livello di rete è stato eseguito con il tag <script>.

Tuttavia, ora ho bisogno di caricare lo script nel documento principale; non può più essere in un iframe. Quindi ho bisogno di un modo diverso per attivare il codice non appena il livello di rete ha rinunciato a cercare di recuperare lo script.

Ho letto "Deep dive into the murky waters of script loading" nel tentativo di capire quali garanzie di ordinazione posso contare su tutti i browser.

Se ho capito le tecniche documentate lì:

  • ho bisogno di mettere il mio codice di errore di gestione in un file separato .js.
  • Quindi, su determinati browser posso garantire che il mio codice venga eseguito solo dopo che lo script di terze parti è stato eseguito o ha avuto esito negativo. Ciò richiede i browser che supportano sia:
    • impostazione dell'attributo <script async> false tramite il DOM,
    • o utilizzando <script onreadystatechange=...> su IE 6+.

Nonostante guardando il async support table, non posso dire se posso contare su sceneggiatura ordinamento in numero sufficiente i browser per questo sia fattibile.

Quindi, come posso gestire in modo affidabile l'errore durante il caricamento di uno script che non controllo?

+0

che dire di verificare l'esistenza di qualcosa nella sceneggiatura. Come se lo script contenga un certo oggetto, cerca quello –

+0

@CrayonViolent, è facile determinare che lo script è stato caricato. Tuttavia, ho bisogno di sapere quando controllare. Se controllo troppo presto, lo script non è stato ancora scaricato. Devo sapere come distinguere tra "non è ancora stato caricato" e "non verrà mai caricato perché ci siamo arresi". Se posso essere sicuro che il mio controllo venga eseguito dopo che lo script è stato elaborato, allora sono tutto pronto. –

+0

beh, imo il controllo di una cosa unica all'interno dello script sarà il tuo modo più affidabile. Ma davvero non penso che ci sia un modo per farlo senza usare 'setTimeout' ogni 100ms circa per x quantità di tempo e poi rinunciare. Questo è quello che faccio in situazioni come questa, e finora non ho trovato un metodo migliore; Non penso che ne esista uno in questo momento:/ –

risposta

2

Credo di aver risolto la questione ho chiesto, anche se si scopre questo non risolve il problema in realtà ho avuto. Oh bene. Ecco la mia soluzione:

Vogliamo eseguire qualche codice dopo che il browser ha completato il tentativo di caricare uno script di terze parti, in modo che possiamo verificare se è stato caricato correttamente. Lo realizziamo limitando il carico di uno script di fallback che si verifica solo dopo che lo script di terze parti è stato eseguito o non è riuscito. Lo script di fallback può quindi verificare se lo script di terze parti ha creato i globals che avrebbe dovuto.

Caricamento di script in ordine del cross-browser ispirato a http://www.html5rocks.com/en/tutorials/speed/script-loading/.

var fallbackLoader = doc.createElement(script), 
    thirdPartyLoader = doc.createElement(script), 
    thirdPartySrc = '<URL to third party script>', 
    firstScript = doc.getElementsByTagName(script)[0]; 

// Doesn't matter when we fetch the fallback script, as long as 
// it doesn't run early, so just set src once. 
fallbackLoader.src = '<URL to fallback script>'; 

// IE starts fetching the fallback script here. 

if('async' in firstScript) { 
    // Browser support for script.async: 
    // http://caniuse.com/#search=async 
    // 
    // By declaring both script tags non-async, we assert 
    // that they need to run in the order that they're added 
    // to the DOM. 
    fallbackLoader.async = thirdPartyLoader.async = false; 
    thirdPartyLoader.src = thirdPartySrc; 
    doc.head.appendChild(thirdPartyLoader); 
    doc.head.appendChild(fallbackLoader); 
} else if(firstScript.readyState) { 
    // Use readyState for IE 6-9. (IE 10+ supports async.) 
    // This lets us fetch both scripts but refrain from 
    // running them until we know that the fetch attempt has 
    // finished for the first one. 
    thirdPartyLoader.onreadystatechange = function() { 
     if(thirdPartyLoader.readyState == 'loaded') { 
      thirdPartyLoader.onreadystatechange = null; 
      // The script-loading tutorial comments: 
      // "can't just appendChild, old IE bug 
      // if element isn't closed" 
      firstScript.parentNode.insertBefore(thirdPartyLoader, firstScript); 
      firstScript.parentNode.insertBefore(fallbackLoader, firstScript); 
     } 
    }; 
    // Don't set src until we've attached the 
    // readystatechange handler, or we could miss the event. 
    thirdPartyLoader.src = thirdPartySrc; 
} else { 
    // If the browser doesn't support async or readyState, we 
    // just won't worry about the case where script loading 
    // fails. This is <14% of browsers worldwide according to 
    // caniuse.com, and hopefully script loading will succeed 
    // often enough for them that this isn't a problem. 
    // 
    // If that isn't good enough, you might try setting an 
    // onerror listener in this case. That still may not work, 
    // but might get another small percentage of old browsers. 
    // See 
    // http://blog.lexspoon.org/2009/12/detecting-download-failures-with-script.html 
    thirdPartyLoader.src = thirdPartySrc; 
    firstScript.parentNode.insertBefore(thirdPartyLoader, firstScript); 
} 
0

Hai considerato l'utilizzo del gestore onerror della finestra? Ciò ti consentirà di rilevare quando si verificano la maggior parte degli errori e puoi quindi agire in modo appropriato. Come ripiego per eventuali problemi non rilevati in questo modo, puoi anche proteggere il tuo codice con try/catch.

Si dovrebbe anche controllare che lo script di terze parti effettivamente caricata:

<script type="text/javascript" onload="loaded=1" src="thirdparty.js"></script> 

Quindi verificare se è caricato:

window.onload = function myLoadHandler() { 
    if (loaded == null) { 
    // The script doesn't exist or couldn't be loaded! 
    } 
} 

È possibile controllare quale script ha causato l'errore utilizzando il parametro url.

window.onerror = function myErrorHandler(errorMsg, url, lineNumber) { 
    if (url == third_party_script_url) { 
    // Do alternate code 
    } else { 
    return false; // Do default error handling 
    } 
} 
+0

Puoi dare un esempio di rilevamento portabile degli errori di script, ma solo quegli errori da un particolare tag '