<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    emu in blogjava

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

    1 事先約定前后臺接口。這樣帶來了很強的前后臺偶合,后臺程序需要知道前臺想要做什么,接口很難一致化,一般不同的服務程序要使用不同的接口。而且如果需要同時并發調用同一個服務程序幾次,那么一樣無法解決接口沖突問題。

    2 前臺動態生成回調接口后把接口名稱傳遞給后臺程序,后臺程序根據接受到的接口名稱動態生成回調接口,比如google就喜歡接受callback參數: 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
    這樣也是一個無奈之舉,一樣避免不了的令人生厭的前后臺偶合,只是改變了偶合的方式,前后臺需要換一種方式的約定,而且如果要解決并行多個異步回調的接口沖突問題,就要動態的給每個回調函數創建一個個不同的名稱,此外服務程序的輸出不允許靜態化,必須有接受參數和生成回調腳本的功能。

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

    emu在這個問題上花費過無數心血后最終還是放棄了,直到昨晚,舜子才終于有了突破:

    <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 的訪問量:
    <span id="123456"></span><BR>
    2543061 的訪問量:
    <span id="2543061"></span><BR>
    20050606 的訪問量:
    <span id="20050606"></span><BR>
    </BODY>
    </HTML>


    如果需要支持錯誤處理,就稍微麻煩一點了,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();//如果數據被cache,運行到這一行的時候有可能回調已經完成,窗口已經關閉。
            }
        }
    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 
    = "無法連接到服務器";
                }
            }(id);
            loadjs(url,callback,errcallback);
        }
    }
    </SCRIPT>
    </HEAD>
    <BODY onload="init()">
    123456 的訪問量:
    <span id="123456"></span><BR>
    2543061 的訪問量:
    <span id="2543061"></span><BR>
    20050606 的訪問量:
    <span id="20050606"></span><BR>
    </BODY>
    </HTML>



    在IE/FIREFOX/OPERA/SAFARI上運行通過。

    這里有幾點說明:IE其實也可以用iframe(試試強行給isIE變量賦false值),不過用iframe的缺點是phantom click(會發出一個頁面跳轉的小聲音)和throbber of doom(應該是指小沙漏型的下載圖標吧?)。

    用document fragment的好處是避免了IE7默認安全模式下面禁止ActiveX的問題。不過利用了IE的一個特點:document fragment不append到document的dom里面的時候,也可以擁有自己的腳本運行空間,可以用script標簽發起請求。這樣用document fragment就可以比iframe使用更少的客戶端資源來完成操作。

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

    firefox下面的script標簽其實支持onerror事件(可以寫在標簽里面或者addEventListener上去),其他瀏覽器根據版本的不同對此有不同程度的支持,所以emu決定利用script標簽可以堵塞頁面運行過程的做法,script標簽后面添加延遲的錯誤處理邏輯(在正確的情形下搶先清空掉iframe的內容來取消這個邏輯)。

    posted on 2007-07-10 10:07 emu 閱讀(3126) 評論(11)  編輯  收藏 所屬分類: DHTML和JAVASCRIPT 技術

    評論

    # re: 腳本綁定回調:不可能完成的任務 2007-07-18 22:22 emu
    寫了個頁面測試三種不同的創建document的方式的時間耗費:

    <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上基本上是個1:10:100的比例。在firefox上iframe的速度和IE差不多,documentfragment的速度快一些,但是不能創建變量空間,沒有什么意義。safari創建documentfragment更快,可是創建iframe更慢了。最慘的是opera,居然每次插入不可見的iframe都會導致頁面重新渲染,最終瀏覽器都掛掉了。  回復  更多評論
      

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

    因此您說的“事實上只有做到這點,json才能真正想xml一樣變成一個純粹的數據描述方式,擺脫對具體上下文程序的依賴,讓一個數據自由的被不同目的的頁面mashup。比如說,在一個頁面上用json結合腳本技術,把來自不同網站的相同格式的json數據合并顯示到一個頁面上。”其實也站不住腳。或者說,要達到這個目標的前提是,都采用你的這個方案。問題是,如果可以都采用一種方案,那怎么樣都行,不是嗎?

    總之,只是一個callback包裝——如果要包裝的話,不用iframe這樣的技巧,其實也可以辦到。只要用你包裝好的回調,沒有什么是辦不到的。  回復  更多評論
      

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

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

    因為不用iframe,你也可以達到向最終編程者隱藏callback名字的目標,只需要callback是由你的框架托管和包裝起來就可以了。差別只在于,這種方式,這個callback名字會污染當前的global上的命名空間,iframe則不會,因為它在一個獨立的scope里——但是付出的代價是有獨立的browserContext(包括window、document等對象)。  回復  更多評論
      

    # re: 腳本綁定回調:不可能完成的任務 2008-03-13 10:36 hax
    因此,iframe只是一個錦上添花的工具。我認為理想的目標是:

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

    那么在一個iframe情況下,你說的“多個一模一樣的callback”的沖突還是存在。這就需要用其他技巧解決。所以反過來,如果其他技巧解決了這個問題,那么iframe的唯一好處就是一丁點都不污染全局命名空間——個人來講,我認為這個好處還是相當有限的。

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

    當然這只是一個一般的描述,實際做的時候,肯定有很多問題要解決,但是從大體上看,應該是行得通的。

      回復  更多評論
      

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

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

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

    另外我測試發現,firefox不支持相對路徑,safari不支持javascript:這種方法寫入iframe,需要write的  回復  更多評論
      

    # re: 腳本綁定回調:不可能完成的任務 2009-04-24 13:25 boyszz
    簡單事情復雜化的典型表現。  回復  更多評論
      

    # re: 腳本綁定回調:不可能完成的任務[未登錄] 2009-05-20 01:14 小風
    后臺直接吐出一個json變量似乎也可以解決:

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

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

    好像上面也有和我想的一樣的  回復  更多評論
      

    # re: 腳本綁定回調:不可能完成的任務 2009-05-22 11:39 emu
    IE8beta版本不支持documentFragment擁有獨立的運行空間,正式版又解決了這個問題,因此寫了個小判斷:

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

    # re: 腳本綁定回調:不可能完成的任務 2010-06-24 11:47 adv
    js + as3也可解決此種問題,缺點是瀏覽器需要播放器支持。
    1。先從as3注冊出一個方法供js調用,作用就是將請求的url轉入到as中,通過as轉發請求。
    3。定義js函數供as調用,來結束請求的返回結果和對應的url地址
    3。擴展as的URLLoader增加一個url屬性來標識發生請求的url。
    4。將結果和對應的url再次傳入js方法。

    如此,可以將并發請求的返回進行對應關系(并非對應順序)。  回復  更多評論
      

    主站蜘蛛池模板: 亚洲区日韩区无码区| 666精品国产精品亚洲| a级毛片免费高清毛片视频| 亚洲第一成年网站大全亚洲| 妞干网在线免费观看| a免费毛片在线播放| 亚洲春色在线观看| 国产乱人免费视频| 88av免费观看| 免费人成网站永久| 亚洲精品在线视频观看| 亚洲AV日韩精品一区二区三区| 四虎成人精品永久免费AV| 亚洲国产精品99久久久久久| 亚洲αv久久久噜噜噜噜噜| 午夜两性色视频免费网站| 在线观看黄片免费入口不卡| 亚洲国产欧美日韩精品一区二区三区| 国产啪亚洲国产精品无码| 四虎国产精品免费久久| 亚洲免费人成在线视频观看 | 亚洲日韩乱码中文无码蜜桃臀| 国产精品另类激情久久久免费| 色欲色香天天天综合网站免费| 在线观看亚洲视频| 亚洲国产成人精品无码一区二区| 亚洲成人影院在线观看| 手机在线免费视频| 在线看片免费人成视久网| 亚洲第一视频在线观看免费| 亚洲国产成人精品无码区花野真一| 亚洲人成依人成综合网| 亚洲?v女人的天堂在线观看 | 亚洲香蕉免费有线视频| 国产成人综合亚洲AV第一页| 青青草国产免费久久久91| 亚洲免费福利视频| 免费萌白酱国产一区二区三区| 鲁啊鲁在线视频免费播放| 亚洲人成网亚洲欧洲无码| 亚洲成a人片7777|