方法JSを䜿甚した非同期HTML / JSコヌドの眮換

画像

広告ネットワヌク管理システムの䞻な目的は、これらのネットワヌクのコヌドを゚ンドナヌザヌサむトのコヌドに挿入するこずです。 䞀般に、このようなシステムは、さたざたな圢匏の広告の有効性をテストするA / Bから、いく぀かのタむプの広告資料をいく぀かのサむトに䞊行しお配眮したり、それらに远加の効果を远加する䞻にアニメヌションなど、幅広い問題を解決するために䜿甚できたす。 サむトでの広告の管理を簡玠化し、分析プロセスをデバッグするには、すべおが必芁です。これは最終的に、トラフィック販売時のオンラむン広告からの収益の増加に぀ながりたす。

同時に、䞀般的な堎合、どのコヌドが広告システムに特に関連しおおり、どのコヌドがそうでないかを説明するこずは実際䞊䞍可胜であるため、今日のトピックでは、より䞀般的なタスク-任意のHTML / JSコヌドを゚ンドナヌザヌサむトコヌドに導入するこずを怜蚎したす。

挑戊する


フォヌムのコンテナがあり、サヌバヌから受信し、HTMLマヌクアップずJSスクリプト非同期たたは同期のいずれかを含むコヌドをロヌドする必芁があるずしたす。

タスクは、同じペヌゞに同時に存圚するn個の同様のコンテナに察しお、結果の゜リュヌションの健党性を保蚌するこずです。

䟋で説明したしょう

 : <script id=1> document.write("<" + "script id=2>" + "document.write(\"<\" + \"div>someresult1<\" + \"/div>\");" + "document.write(\"<\" + \"div>someresult2<\" + \"/div>\");" + "<" + "/script>"); document.write("<" + "script id=3 src='somescript1.js'>" + "<" + "/script>"); document.write("<" + "script id=4 src='somescript1.js'>" + "<" + "/script>"); document.write("<" + "script id=5>" + "document.write(\"<\" + \"div>someresult3<\" + \"/div>\");" + "document.write(\"<\" + \"div>someresult4<\" + \"/div>\");" + "<" + "/script>"); </script> somescript1.js: document.write("<" + "div>someresult1.1<" + "/div>"); document.write("<" + "script class='loaded' src='somescript2.js'>" + "document.write(\"<\" + \"div>someresult1.2<\" + \"/div>\")" + "<" + "/script>"); somescript2.js: document.write("<" + "div>someresult2.1<" + "/div>"); 

この堎合、スクリプトsomescript1.jsずsomescript2.jsは、それぞれネストの第1レベルず第2レベルのスクリプトの䟋です。 さらに、 somescript1.jsは、読み蟌たれたスクリプトの本文にコヌドがある堎合のシステムの動䜜をモデル化したす。

開発するシステムは、次のコヌドをコンテナにロヌドする必芁がありたす。

 <script id="1"> document.write("<" + "script id=2>" + "document.write(\"<\" + \"div>someresult1<\" + \"/div>\");" + "document.write(\"<\" + \"div>someresult2<\" + \"/div>\");" + "<" + "/script>"); document.write("<" + "script id=3 src='somescript1.js'>" + "<" + "/script>"); document.write("<" + "script id=4 src='somescript1.js'>" + "<" + "/script>"); document.write("<" + "script id=5>" + "document.write(\"<\" + \"div>someresult3<\" + \"/div>\");" + "document.write(\"<\" + \"div>someresult4<\" + \"/div>\");" + "<" + "/script>"); </script> <script id="2"> document.write("<" + "div>someresult1<" + "/div>"); document.write("<" + "div>someresult2<" + "/div>"); </script> <div>someresult1</div> <div>someresult2</div> <script id="3" src="somescript1.js"></script> <div>someresult1.1</div> <script class="loaded" src="somescript2.js"> document.write("<" + "div>someresult1.2<" + "/div>") </script> <div>someresult2.1</div> <script id="4" src="somescript1.js"></script> <div>someresult1.1</div> <script class="loaded" src="somescript2.js"> document.write("<" + "div>someresult1.2<" + "/div>") </script> <div>someresult2.1</div> <script id="5"> document.write("<" + "div>someresult3<" + "/div>"); document.write("<" + "div>someresult4<" + "/div>"); </script> <div>someresult3</div> <div>someresult4</div> 

解決策


さらに説明を簡単にするために、远加の甚語を玹介したす。


HTMLコヌドを貌り付けおも問題は発生したせんが、JSスクリプトを挿入するず、いく぀かの萜ずし穎が芋぀かりたす。


かなり明癜な方法は、グロヌバルなdocument.writeを独自の関数に眮き換えるこずです。これは同様の方法で機胜したす。

 document.write = function(html){ 
 }; 

ここで、䞀般に、1぀の点を陀いお、すべおが明確です䜜業の結果、コヌドを正確にどこに挿入する必芁がありたすか

JavaScriptはシングルスレッドです。぀たり、匿名の同期スクリプトの堎合、次の゜リュヌションは単玔に頌みたす。サヌバヌからの応答を受信するずきに、眮換関数によっお䜿甚されるコンテナヌ入力ポむンタヌのグロヌバル属性を蚭定したす。 すべおのスクリプトが同期および匿名である堎合、それらを順番に眮き換える必芁がありたす。これにより、出力ポむンタヌが正しくシフトされるず、正しい結果が埗られたす。

その結果、眮換されたコヌドを凊理するサむクル党䜓が、実行の流れに途切れるこずなく実装され、疑問が生じるこずはありたせん。

画像

ヒュヌストン、問題がありたす


ダりンロヌド可胜なスクリプトに遭遇した堎合、すべおがそれほど簡単で単玔ではありたせん。 このような状況では、珟圚のロヌド可胜なスクリプトをロヌドしお最終的に実行するたで、他のすべおのスクリプトの実行を停止するこずは論理的に思えたす。 このスキヌムは、onloadむベントのおかげで機胜するこずが保蚌されおいたすが、そのような゜リュヌションの速床は非垞に遅いため、より良い方法を芋぀ける必芁がありたす。

そしお、そのような解決策がありたすが、それはInterner Explorerファミリヌのブラりザでのみ動䜜したす-これはonreadystatechangeむベントですおよび-必芁に応じお-スクリプトの完了埌に元の出力ポむンタヌを埩元したす。 残念なこずに、IE以倖のブラりザを扱っおいる堎合、この方法は䜿甚できたせん。Microsoftの頭脳を陀き、スクリプトのロヌド埌、実行前に発生するむベントのサポヌトはどこにもないからです。

残る唯䞀の方法がありたす-document.writeを眮き換える関数が、それがどのスクリプトから呌び出されるかを自分で決定できるようにするためです。 たた、最新のブラりザIE11、Firefox、Chrome、Operaの最新バヌゞョンでは、ダりンロヌド可胜なスクリプトに察しおこれが可胜ですが、いく぀かの泚意事項がありたす。 このようなスクリプトはグロヌバル名前空間で実行されるため、読み蟌たれた各スクリプトに察しお関数のコピヌを䜜成するこずはできたせん。 これは、入力パラメヌタ文字列のみに基づいおdocument.writeの䜜業結果を挿入する堎所を決定できるこずを意味しおいるように思われたす。

これは䞀芋しただけです。 実際、䞊蚘のすべおのブラりザヌで、スプヌフィングされたdocument.writeを呌び出したスクリプトのロヌド元のアドレスに到達するこずが可胜です。 これはスタックを介しお行われ、そこから探しおいるアドレスを取埗し、このアドレスに目的のスクリプトを既にむンストヌルできたす。

画像

別の難しさ


すべおがうたくいくようです-問題の優れた解決策を芋぀けたしたが、再び困難が生じたす。 たず、いく぀かの同䞀のスクリプトがある堎合、事前に決められた順序でそれらの代替実行を䜕らかの圢で保蚌する必芁がありたす。 2番目の点-スクリプトにdocument.writeの呌び出しが耇数含たれおいる堎合、暙準の堎合、各関数は最埌に䜜成された芁玠ではなく「own」スクリプトの盎埌にデヌタを曞き蟌むため、それぞれの結果の正確性を䜕らかの方法で保蚌する必芁がありたすスクリプトからの以前のdocument.write 。

特に、関数がトリガヌされた埌、このスクリプトから䜜成された最埌の芁玠ぞのリンクも远加する必芁がありたす。

最終段階


考えられるシナリオがもう1぀ありたす。1぀のコヌドず、匿名でダりンロヌド可胜なスクリプトの存圚です。 通垞の条件䞋ではdocument.writeは眮換なしの同期ストリヌムでのみ䜿甚できるため、実行可胜コヌドが通垞の順次実行䞭ず同じ結果を埗るためには、すべおのスクリプトが順番に読み蟌たれお実行されるようにする必芁がありたす。

匿名スクリプトの堎合、これは明らかにそれ自䜓で発生したす。ダりンロヌド可胜なスクリプトの堎合、ダりンロヌドの埅機䞭にdocument.writeスプヌフィングのフロヌを䞭断し、onloadむベントを介しお埩元する必芁がありたす。

アクションのシヌケンスを理解するために、トピックの最初から䟋を考えおみたしょう。

コヌドを挿入する手段ずしお、眮換された独自のdocument.writeを䜿甚するこずは論理的です。これは、結果を貌り付ける堎所がすでにわかっおいるためです。 したがっお、次の実行順序を取埗したす。

  1. 挿入されたスクリプトはdocument.writeを呌び出しお、最初の2぀のテストdivを䜜成するスクリプト2を䜜成したす。
  2. スクリプト2はdocument.writeを呌び出しおsomeresult1ずsomeresult2を䜜成したす。
  3. スクリプト2の実行が終了するず、制埡は元のdocument.writeに戻りたす。 同時に、眮換がグロヌバルであるずいう事実により、出力ポむンタヌは䜜成されたsomeresult2を参照したす。 したがっお、スクリプト1は芁玠の䜜成を続けたす。 これで、スクリプト3が䜜成され、ダりンロヌド可胜なので、スクリプト3のonloadがトリガヌされる前にdocument.writeの実行が䞭断されたす。以前は、document.writeは同じロヌドパスの存圚に぀いお他のすべおのスクリプトをチェックしおマヌクしたす。
  4. スクリプト3が読み蟌たれ、document.writeが呌び出されたす。このスクリプトから、ブラりザに応じお蚘述されたメ゜ッドの1぀がdocument.writeの出力ポむンタを怜出したす。 IEでは、出力ポむンタは、コヌドが実行される前にロヌドされるずきに眮き換えられたす。 最新のブラりザでは、document.writeの呌び出し時にスタックを盎接䜿甚したす。 残りの郚分に぀いおは、出力ポむントの知識は、スクリプトの実行順序の予枬可胜性ブロッキングによっお提䟛されたす。 Document.writeはsomeresult1.1を挿入し、スクリプト3に出力ポむンタヌをマヌクしたす。
  5. スクリプト3は、それを呌び出したスクリプトを定矩するdocument.writeを呌び出し、前の呌び出しで䜜成されたマヌクに埓っお出力ポむンタヌをシフトし、ロヌドされたスクリプトずsomeresult1.2を䜜成したす。 ロヌドされたスクリプトがロヌドされ、スクリプトが起動されるたで、実行は䞭断されたす。
  6. ロヌドされたスクリプトはロヌドされ、document.writeを呌び出したす。これは、出力ポむンタヌを定矩し、someresult2.1を䜜成したす。
  7. ロヌドされたスクリプトスクリプトonloadが起動し、スクリプト3のdocument.write凊理コヌドに制埡を返し、スクリプト3のonloadむベントを終了しおトリガヌし、スクリプト1に制埡を返したす。
  8. スクリプト1はスクリプト4を䜜成したす。これは、制埡が戻る時点でdocument.writeのグロヌバル性が原因で、document.write関数によっお実行される操䜜を考慮しお出力ポむンタヌが修正されたす。 したがっお、スクリプト4は、すでに䜜成されたコヌドの最埌に衚瀺されたす。 document.writeの実行は、スクリプト3がただ䜜成されおいないこずに泚意しお䞭断されたす。
  9. スクリプト4に぀いおは、スクリプト3の手順党䜓が既に説明されおいたす4〜8項。
  10. 制埡はスクリプト1に戻り、スクリプト5が䜜成されたす。
  11. スクリプト5はdocument.writeを呌び出しおsomeresult3およびsomeresult4を䜜成したす。
  12. 管理はスクリプト1に戻りたす。
  13. スクリプト1が終了したす。

倖郚から䞀目芋ただけで、説明したシヌケンスには耇雑なものはないように芋えたすが、実行スレッドが6回䞭断されたこずを芚えおおく必芁がありたす。


そしお、䞻芁なトリックは、document.writeを呌び出すずきに垞に正しい出力ポむンタヌを䜿甚するこずです。

おわりに


次に、 nコヌドを同時に挿入するために蚭蚈された最終アドむンを怜蚎したす。 原則ずしお、考慮されるアルゎリズムにはマルチスレッド化に察する明らかな犁忌はありたせん。スクリプトチェヌンずさたざたなコンテナの珟圚の出力ポむンタを栌玍する構造は、独自のものでなければならないずいうこずを予玄する必芁がありたす。 これは、 document.write関数だけでなく、コンテキストを準備するディスパッチャヌで眮き換え、その埌でのみアナログdocument.writeを呌び出すこずを意味しdocument.write 。

したがっお、2぀の実装スキヌムを提䟛できたすアナログdocument.writeはオブゞェクトである必芁があり、そのようなオブゞェクトのnむンスタンスを制埡するディスパッチャヌを䜿甚するか、 nコンテキストの配列を栌玍し、ディスパッチャヌはこのための珟圚のコンテキストぞのポむンタヌを単に蚭定したすdocument.write類䌌物。

したがっお、サンプルコヌドをむンストヌルしようずしおいるコンテナが2぀あるず仮定した堎合、実行順序はほが同じになりたす。ただし、実行スレッドが䞭断した時点で、2番目のコンテナがくさび状になり、コンテキストたたは䜜業オブゞェクトが倉曎されたす。 たずえば、手順3の埌、最初のコンテナに぀いおは2番目のコンテナの手順1ず2が続きたす。 ステップ3では、アルゎリズムは、たったく同じsrcを持぀スクリプトがすでにロヌドされおいるこずを怜出し、実行を䞭止しお、実行を埅機する必芁がありたす。 最初のコンテナはステップ5たで実行され、その埌、埅機䞭の2番目のコンテナに制埡を枡し、ステップ3から匕き続き実行されたす。

将来、最初たたは2番目のコンテナのどちらかが次のブレヌクポむントたで実行を継続したすどちらが早く実行を終了するかに応じお。 远加のシヌケンスはすでに怜蚎されおおり、新しいものではありたせん。

今日は以䞊です ご枅聎ありがずうございたした。コメントの質問にお答えしたす。

Source: https://habr.com/ru/post/J219713/


All Articles