emu in blogjava

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            171 隨筆 :: 103 文章 :: 1052 評(píng)論 :: 2 Trackbacks
          如果不用xmlhttp方式獲取json數(shù)據(jù),一般我們最好用的方式是用script標(biāo)簽直接引用需要的腳本。但是不像xmlhttp可以很容易的把請(qǐng)求數(shù)據(jù)腳本和請(qǐng)求到的數(shù)據(jù)綁定到一起,script標(biāo)簽本身是無(wú)法獲知自己獲得了什么數(shù)據(jù)的,這個(gè)問(wèn)題上一般使用的解決方案有:

          1 事先約定前后臺(tái)接口。這樣帶來(lái)了很強(qiáng)的前后臺(tái)偶合,后臺(tái)程序需要知道前臺(tái)想要做什么,接口很難一致化,一般不同的服務(wù)程序要使用不同的接口。而且如果需要同時(shí)并發(fā)調(diào)用同一個(gè)服務(wù)程序幾次,那么一樣無(wú)法解決接口沖突問(wèn)題。

          2 前臺(tái)動(dòng)態(tài)生成回調(diào)接口后把接口名稱(chēng)傳遞給后臺(tái)程序,后臺(tái)程序根據(jù)接受到的接口名稱(chēng)動(dòng)態(tài)生成回調(diào)接口,比如google就喜歡接受callback參數(shù): http://www.google.com/reader/public/javascript/user/10949413115399023739/label/officialgoogleblogs?n=10&callback=test
          飯否的接口也是這樣的:
          http://api.fanfou.com/statuses/user_timeline.json?callback=test
          這樣也是一個(gè)無(wú)奈之舉,一樣避免不了的令人生厭的前后臺(tái)偶合,只是改變了偶合的方式,前后臺(tái)需要換一種方式的約定,而且如果要解決并行多個(gè)異步回調(diào)的接口沖突問(wèn)題,就要?jiǎng)討B(tài)的給每個(gè)回調(diào)函數(shù)創(chuàng)建一個(gè)個(gè)不同的名稱(chēng),此外服務(wù)程序的輸出不允許靜態(tài)化,必須有接受參數(shù)和生成回調(diào)腳本的功能。

          假如我們想要像生成靜態(tài)rss(http://api.fanfou.com/statuses/user_timeline.rss)文件一樣的生成靜態(tài)的json(http://api.fanfou.com/statuses/user_timeline.json)又不希望或者不能使用xmlhttp來(lái)拉取json字符串,而想要用一致的callback接口來(lái)回傳數(shù)據(jù),那么怎么樣才能解決接口沖突問(wèn)題呢?事實(shí)上只有做到這點(diǎn),json才能真正想xml一樣變成一個(gè)純粹的數(shù)據(jù)描述方式,擺脫對(duì)具體上下文程序的依賴(lài),讓一個(gè)數(shù)據(jù)自由的被不同目的的頁(yè)面mashup。比如說(shuō),在一個(gè)頁(yè)面上用json結(jié)合腳本技術(shù),把來(lái)自不同網(wǎng)站的相同格式的json數(shù)據(jù)合并顯示到一個(gè)頁(yè)面上。

          emu在這個(gè)問(wèn)題上花費(fèi)過(guò)無(wú)數(shù)心血后最終還是放棄了,直到昨晚,舜子才終于有了突破:

          <HTML>
          <HEAD>
          <SCRIPT LANGUAGE="JavaScript">
          function loadjs(url,callback){
              
          if(window.ActiveXObject){
                  
          var df = document.createDocumentFragment();    
                  df.visitCountCallBack 
          = callback
                  
          var s = document.createElement("script");
                  df.appendChild(s)
                  s.src
          =url;
              }
          else{
                  
          var i = document.createElement("IFRAME");    
                  i.callbackID 
          = "2";
                  i.style.display
          ="none";
                  i.callback
          =callback;
                  i.src
          ="javascript:\"<script>function visitCountCallBack(o){frameElement.callback(o)}<\/script><script src='"+url+"'><\/script>\""
                  document.body.appendChild(i);
                  i.contentWindow.callback 
          = callback
              }
          }

          function init(){
              
          var spans = document.getElementsByTagName("span");
              
          for(var i=0;i<spans.length;i++){
                  
          var id = spans[i].id;
                  
          var url = "http://g2.qzone.qq.com/fcg-bin/cgi_emotion_list.fcg?uin="+id;
                  
          var callback = function(id){ return function(data){
                      document.getElementById(id).innerHTML 
          = data.visitcount;
                      }
                  }(id);
                  loadjs(url,callback);
              }
          }
          </SCRIPT>
          </HEAD>
          <BODY onload="init()">
          123456 的訪問(wèn)量:
          <span id="123456"></span><BR>
          2543061 的訪問(wèn)量:
          <span id="2543061"></span><BR>
          20050606 的訪問(wèn)量:
          <span id="20050606"></span><BR>
          </BODY>
          </HTML>


          如果需要支持錯(cuò)誤處理,就稍微麻煩一點(diǎn)了,emu的做法是這樣的: 

           

           

          <HTML>
          <HEAD>
          <SCRIPT LANGUAGE="JavaScript">
          var isIE = !!window.ActiveXObject;
          var useFragment=false;
          function loadjs(url,callback,errcallback){
              
          if(isIE){
                  
          if(useFragment){
                     
          var df = document.createDocumentFragment();    
                      df.visitCountCallBack 
          = function(data){
                          s.onreadystatechange
          =null;
                          df
          =null;
                          callback(data);
                      }
                      
          var s = df.createElement("SCRIPT");
                      df.appendChild(s);
                      s.onreadystatechange
          =function(){
                          
          if(s.readyState=="loaded") {
                              s.onreadystatechange
          =null;
                              df
          =null;
                              errcallback();
                          }
                      }
                      s.src 
          = url;
                  }
          else{
                      
          var i=new ActiveXObject("htmlfile");
                      i.open();
                      i.parentWindow.visitCountCallBack
          =function(i){
                          
          return function(d){
                              i.parentWindow.errcallback
          =null;
                              i
          =null;
                              callback(d);
                          }
                      }(i);
                      i.parentWindow.errcallback
          =function(d){
                          i.parentWindow.errcallback
          =null;
                          i
          =null;
                          errcallback(d);
                      }
                      i.write(
          "<script src=\""+url+"\"><\/script><script defer>setTimeout(\"errcallback()\",0)<\/script>")
                      
          if(i)i.close();//如果數(shù)據(jù)被cache,運(yùn)行到這一行的時(shí)候有可能回調(diào)已經(jīng)完成,窗口已經(jīng)關(guān)閉。
                  }
              }
          else{
                  
          var i = document.createElement("IFRAME");    
                  i.style.display
          ="none";
                  i.callback
          =function(o){
                      callback(o);
                      i.contentWindow.callback
          =null;
                      i.src
          ="about:blank"
                      i.parentNode.removeChild(i);
                      i 
          = null;
                  };
                  i.errcallback 
          = errcallback;
                  i.src
          ="javascript:\"<script>function visitCountCallBack(data){frameElement.callback(data)};<\/script><script src='"+url+"'><\/script><script>setTimeout('frameElement.errcallback()',0)<\/script>\"";
                  document.body.appendChild(i);
              }
          }

          function init(){
              
          var spans = document.getElementsByTagName("span");
              
          for(var i=0;i<spans.length;i++){
                  
          var id = spans[i].id;
                  
          var url = "http://g2.qzone.qq.com/fcg-bin/cgi_emotion_list.fcg?uin="+id;
                  
          var callback = function(id){ return function(data){
                      document.getElementById(id).innerHTML 
          = data.visitcount;
                      }
                  }(id);
                  
          var errcallback = function(id){ return function(){
                      document.getElementById(id).innerHTML 
          = "無(wú)法連接到服務(wù)器";
                      }
                  }(id);
                  loadjs(url,callback,errcallback);
              }
          }
          </SCRIPT>
          </HEAD>
          <BODY onload="init()">
          123456 的訪問(wèn)量:
          <span id="123456"></span><BR>
          2543061 的訪問(wèn)量:
          <span id="2543061"></span><BR>
          20050606 的訪問(wèn)量:
          <span id="20050606"></span><BR>
          </BODY>
          </HTML>



          在IE/FIREFOX/OPERA/SAFARI上運(yùn)行通過(guò)。

          這里有幾點(diǎn)說(shuō)明:IE其實(shí)也可以用iframe(試試強(qiáng)行給isIE變量賦false值),不過(guò)用iframe的缺點(diǎn)是phantom click(會(huì)發(fā)出一個(gè)頁(yè)面跳轉(zhuǎn)的小聲音)和throbber of doom(應(yīng)該是指小沙漏型的下載圖標(biāo)吧?)。

          用document fragment的好處是避免了IE7默認(rèn)安全模式下面禁止ActiveX的問(wèn)題。不過(guò)利用了IE的一個(gè)特點(diǎn):document fragment不append到document的dom里面的時(shí)候,也可以擁有自己的腳本運(yùn)行空間,可以用script標(biāo)簽發(fā)起請(qǐng)求。這樣用document fragment就可以比iframe使用更少的客戶(hù)端資源來(lái)完成操作。

          雖然多個(gè)版本的IE都支持這個(gè)特性,但是emu還是認(rèn)為其他非IE瀏覽器的處理更為合理,為了防止將來(lái)萬(wàn)一IE fix了這個(gè)bug造成措手不及,emu準(zhǔn)備了另外兩個(gè)備用方案,一個(gè)是當(dāng)useFragment被聲明為false的情況下,可以用一個(gè)htmlfile的控件來(lái)代替(google在gmail中使用了這個(gè)控件,但是造成一些用戶(hù)在抱怨IE7下面的安全提示);另一個(gè)是如果不能用ActiveX,還可以走非IE瀏覽器的邏輯,用iframe來(lái)完成操作,但是耗費(fèi)的客戶(hù)端資源要稍微多一點(diǎn)。用iframe另外兩個(gè)的缺點(diǎn)是phantom click(會(huì)發(fā)出一個(gè)頁(yè)面跳轉(zhuǎn)的小聲音)和throbber of doom(應(yīng)該是指小沙漏型的下載圖標(biāo)吧?)。針對(duì)具體的用戶(hù)群的瀏覽器種類(lèi),上面幾種方案不用全上,看需要了。

          firefox下面的script標(biāo)簽其實(shí)支持onerror事件(可以寫(xiě)在標(biāo)簽里面或者addEventListener上去),其他瀏覽器根據(jù)版本的不同對(duì)此有不同程度的支持,所以emu決定利用script標(biāo)簽可以堵塞頁(yè)面運(yùn)行過(guò)程的做法,script標(biāo)簽后面添加延遲的錯(cuò)誤處理邏輯(在正確的情形下?lián)屜惹蹇盏鬷frame的內(nèi)容來(lái)取消這個(gè)邏輯)。

          posted on 2007-07-10 10:07 emu 閱讀(3127) 評(píng)論(11)  編輯  收藏 所屬分類(lèi): DHTML和JAVASCRIPT 技術(shù)

          評(píng)論

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2007-07-18 22:22 emu
          寫(xiě)了個(gè)頁(yè)面測(cè)試三種不同的創(chuàng)建document的方式的時(shí)間耗費(fèi):

          <HTML>
          <HEAD>
          </HEAD>
          <BODY>
          document fragment:<span id="t1"></span><br>
          htmlfile:<span id="t2"></span><br>
          iframe:<span id="t3"></span><br>
          <button onclick="test()">test</button>
          <SCRIPT LANGUAGE="JavaScript">
          var n = 100;
          function test(){
          var t=new Date();
          for(var i=0;i<n;i++)
          document.createDocumentFragment()
          document.getElementById("t1").innerHTML=(new Date()-t);
          if(window.ActiveXObject){
          var t=new Date();
          for(var i=0;i<n;i++)
          new ActiveXObject("htmlfile")
          document.getElementById("t2").innerHTML=(new Date()-t);
          }
          var t=new Date();
          for(var i=0;i<n;i++){
          var tt = document.createElement("IFRAME");
          tt.style.display="none";
          document.body.appendChild(tt);
          }
          document.getElementById("t3").innerHTML=(new Date()-t);
          }
          </SCRIPT>
          </BODY>
          </HTML>

          在ie上基本上是個(gè)1:10:100的比例。在firefox上iframe的速度和IE差不多,documentfragment的速度快一些,但是不能創(chuàng)建變量空間,沒(méi)有什么意義。safari創(chuàng)建documentfragment更快,可是創(chuàng)建iframe更慢了。最慘的是opera,居然每次插入不可見(jiàn)的iframe都會(huì)導(dǎo)致頁(yè)面重新渲染,最終瀏覽器都掛掉了。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-11 19:45 hax
          emu同志,您的這個(gè)方法其實(shí)還是只是對(duì)最終編程者隱藏了callback的名字而已,并沒(méi)有提供多少額外的好處也。callback的名字(比如您的visitCountCallback)歸根到底是省不掉的,只是是否暴露給最終編程者的差別。

          因此您說(shuō)的“事實(shí)上只有做到這點(diǎn),json才能真正想xml一樣變成一個(gè)純粹的數(shù)據(jù)描述方式,擺脫對(duì)具體上下文程序的依賴(lài),讓一個(gè)數(shù)據(jù)自由的被不同目的的頁(yè)面mashup。比如說(shuō),在一個(gè)頁(yè)面上用json結(jié)合腳本技術(shù),把來(lái)自不同網(wǎng)站的相同格式的json數(shù)據(jù)合并顯示到一個(gè)頁(yè)面上。”其實(shí)也站不住腳。或者說(shuō),要達(dá)到這個(gè)目標(biāo)的前提是,都采用你的這個(gè)方案。問(wèn)題是,如果可以都采用一種方案,那怎么樣都行,不是嗎?

          總之,只是一個(gè)callback包裝——如果要包裝的話(huà),不用iframe這樣的技巧,其實(shí)也可以辦到。只要用你包裝好的回調(diào),沒(méi)有什么是辦不到的。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-12 01:39 emu
          嗯,我想差別還是蠻大的。
          1 callback的名字如果暴露給最終編程者,首先如果有多個(gè)一模一樣的callback(但是其中的數(shù)據(jù)不同),難免就要相互沖突。
          2 你所說(shuō)的前提如果是“用callback方式回傳數(shù)據(jù)”的話(huà),其實(shí)這個(gè)方式現(xiàn)在是最流行的方式了。
          3 我的方案本來(lái)也不依賴(lài)iframe。只是不用iframe的話(huà)只能在IE上面用而已。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-13 10:20 hax
          我的意思是,無(wú)論你用iframe或是只能用于ie的documentFragment(這明顯是ie的一個(gè)問(wèn)題,憑什么要為任意一個(gè)新節(jié)點(diǎn)產(chǎn)生一個(gè)新的腳本scope?而且我懷疑這會(huì)引起內(nèi)存泄漏——ms的人在說(shuō)memory leak的patterns那個(gè)文章里曾經(jīng)提到過(guò)由這一問(wèn)題引起的內(nèi)存泄漏),所付出的代價(jià)似乎比不上得到的好處。

          因?yàn)椴挥胕frame,你也可以達(dá)到向最終編程者隱藏callback名字的目標(biāo),只需要callback是由你的框架托管和包裝起來(lái)就可以了。差別只在于,這種方式,這個(gè)callback名字會(huì)污染當(dāng)前的global上的命名空間,iframe則不會(huì),因?yàn)樗谝粋€(gè)獨(dú)立的scope里——但是付出的代價(jià)是有獨(dú)立的browserContext(包括window、document等對(duì)象)。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-13 10:36 hax
          因此,iframe只是一個(gè)錦上添花的工具。我認(rèn)為理想的目標(biāo)是:

          1. callback名字最好是可配置的。因?yàn)槿f(wàn)一有些人寫(xiě)死了callback名字。。。
          2. 有些人不僅寫(xiě)死了,而且不是callback({...json...}),而是var hardCodeJSON = {...json...},最好也能處理這種情況(肯定是可以做到的)。
          3. 不能每次都搞一個(gè)iframe,這樣太浪費(fèi)。iframe的唯一目的其實(shí)是搞一個(gè)獨(dú)立的scope,所以弄一個(gè)iframe就可以了。還有基于前面提到的因素,建議不要用documentFragment。

          那么在一個(gè)iframe情況下,你說(shuō)的“多個(gè)一模一樣的callback”的沖突還是存在。這就需要用其他技巧解決。所以反過(guò)來(lái),如果其他技巧解決了這個(gè)問(wèn)題,那么iframe的唯一好處就是一丁點(diǎn)都不污染全局命名空間——個(gè)人來(lái)講,我認(rèn)為這個(gè)好處還是相當(dāng)有限的。

          下面說(shuō)一個(gè)一般化(也就是不需要iframe)的最土的思路。原本我們插入一個(gè)<script>標(biāo)簽來(lái)讀取json。現(xiàn)在我們可以插入3個(gè)<script>。第一個(gè)做初始化,建立服務(wù)器所需要的callback函數(shù),如果有命名沖突,則把之前的那個(gè)變量保存起來(lái)。第二個(gè)還是load JSON。第三個(gè)做清理,去掉服務(wù)器所用的callback,并把之前的變量恢復(fù)起來(lái)。

          當(dāng)然這只是一個(gè)一般的描述,實(shí)際做的時(shí)候,肯定有很多問(wèn)題要解決,但是從大體上看,應(yīng)該是行得通的。

            回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-13 14:38 emu
          呵呵,假如操作是串行的是行得通的,但是串行的操作本來(lái)也很容易處理命名沖突。如果操作是異步化的,并行發(fā)起的請(qǐng)求,并且請(qǐng)求的發(fā)起順序和相應(yīng)順序是沒(méi)有得到任何保證的,那么你說(shuō)的“如果有命名沖突,則把之前的那個(gè)變量保存起來(lái)”能解決得了什么問(wèn)題呢?
          至于內(nèi)存泄漏問(wèn)題,一方面,內(nèi)存泄漏要靠工具檢查確認(rèn)是否存在,而不是靠猜它可能存在就簡(jiǎn)單的去放棄一個(gè)做法的。二方面,就算發(fā)現(xiàn)了內(nèi)存泄漏,應(yīng)該想辦法定位到內(nèi)存泄漏的原因并解決它。最后,即使真的一時(shí)沒(méi)有辦法解決內(nèi)存泄漏,帶來(lái)的影響還是需要評(píng)估的?比如有的時(shí)候定時(shí)器調(diào)用函數(shù)會(huì)造成32byte的內(nèi)存泄漏,能解決當(dāng)然很好,不解決會(huì)有很大問(wèn)題嗎?IE一關(guān)掉內(nèi)存就收回來(lái)了,用戶(hù)又不會(huì)一直開(kāi)著IE三個(gè)月不關(guān)。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2008-03-13 18:04 我佛山人
          我覺(jué)得這種方式不錯(cuò),而且我改良過(guò)之后,根本不需要建立名為visitCountCallBack的函數(shù),只需要約定var json = {...};

          演示:http://wfsr.net/temp/loadjson.html

          另外我測(cè)試發(fā)現(xiàn),firefox不支持相對(duì)路徑,safari不支持javascript:這種方法寫(xiě)入iframe,需要write的  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2009-04-24 13:25 boyszz
          簡(jiǎn)單事情復(fù)雜化的典型表現(xiàn)。  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù)[未登錄](méi) 2009-05-20 01:14 小風(fēng)
          后臺(tái)直接吐出一個(gè)json變量似乎也可以解決:

          <?php
          echo
          ' var JSON={ ...}; ' ;
          ?>

          然后不是就可以像 jquery 請(qǐng)求返回一個(gè) json 對(duì)象一樣么?
          $.post( url, {}, function(json){
          json ....
          } ,'json' );

          好像上面也有和我想的一樣的  回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2009-05-22 11:39 emu
          IE8beta版本不支持documentFragment擁有獨(dú)立的運(yùn)行空間,正式版又解決了這個(gè)問(wèn)題,因此寫(xiě)了個(gè)小判斷:

          var df = document.createDocumentFragment();
          df.callback=function(){alert("documentFragment有獨(dú)立的腳本運(yùn)行空間")};
          window.callback=function(){alert("不支持documentFragment獨(dú)立腳本運(yùn)行空間")}
          var s = document.createElement("script");
          df.appendChild(s)
          s.src="javascript:'callback()'";
            回復(fù)  更多評(píng)論
            

          # re: 腳本綁定回調(diào):不可能完成的任務(wù) 2010-06-24 11:47 adv
          js + as3也可解決此種問(wèn)題,缺點(diǎn)是瀏覽器需要播放器支持。
          1。先從as3注冊(cè)出一個(gè)方法供js調(diào)用,作用就是將請(qǐng)求的url轉(zhuǎn)入到as中,通過(guò)as轉(zhuǎn)發(fā)請(qǐng)求。
          3。定義js函數(shù)供as調(diào)用,來(lái)結(jié)束請(qǐng)求的返回結(jié)果和對(duì)應(yīng)的url地址
          3。擴(kuò)展as的URLLoader增加一個(gè)url屬性來(lái)標(biāo)識(shí)發(fā)生請(qǐng)求的url。
          4。將結(jié)果和對(duì)應(yīng)的url再次傳入js方法。

          如此,可以將并發(fā)請(qǐng)求的返回進(jìn)行對(duì)應(yīng)關(guān)系(并非對(duì)應(yīng)順序)。  回復(fù)  更多評(píng)論
            

          主站蜘蛛池模板: 云安县| 怀宁县| 海丰县| 溧阳市| 西丰县| 甘泉县| 孟津县| 财经| 揭阳市| 肥城市| 同心县| 福安市| 炎陵县| 洛浦县| 陇川县| 永靖县| 晋城| 巴林右旗| 开江县| 洛扎县| 弥渡县| 哈密市| 临夏县| 西乡县| 宁强县| 新津县| 津市市| 大宁县| 萨嘎县| 织金县| 湖南省| 阳信县| 盘锦市| 久治县| 商丘市| 宜川县| 海城市| 合肥市| 西昌市| 芒康县| 黎川县|