js中的sleep、pause 實現


 
除了Narrative JS,jwacs(Javascript With Advanced Continuation Support)  也致力於通過擴展JavaScript語法來避免編寫讓人頭痛的異步調用的回調函數。用jwacs 實現的sleep,代碼是這樣:

function sleep(msec) 
{
    var k = function_continuation;
    setTimeout(function() { resume k <- mesc; }, msec);
    suspend;
} 


這個語法更嚇人了,而且還是java里不被推薦使用的線程方法名。坦白說我傾向於 Narrative JS。

同Narrative JS一樣,jwacs也需要預編譯,預編譯器是用 LISP 語言編寫。目前也是 Alpha 的版本。兩者的更多介紹和比較可以參閱 SitePoint 上的新文章: Eliminating async Javascript callbacks by preprocessing

編寫復雜的JavaScript腳本時,有時會有需求希望腳本能停滯指定的一段時間,類似於 java 中的 Thread.sleep 或者 sh 腳本中的 sleep 命令所實現的效果。

眾所周知,JavaScript 並沒有提供類似於 Java 的線程控制的功能, 雖然有 setTimeout 和 setInterval 兩個方法可以做一些定時執行控制,但並不能滿足所有的要求。一直以來,都有很多人問如何在JavaScript中實現 sleep/pause/wait ,也確實有些很蹩腳的解決方案:

最簡單也最糟糕的方法就是寫一個循環,代碼可能如下:

function sleep(numberMillis) 
{
    var now = new Date();
    var exitTime = now.getTime() + numberMillis;
    while (true) 
    {
        now = new Date();
        if (now.getTime() > exitTime)
        return;
    }
} 

如上的代碼其實並沒有讓腳本解釋器sleep下來,而且有讓CPU迅速上到高負荷的附作用。瀏覽器甚至會在該段時間內處於假死狀態。

其二有聰明人利用IE特殊的對話框實現來曲徑通幽,代碼可能如下: 

function sleep(timeout)
{
    window.showModalDialog("javascript:document.writeln('<script>window.setTimeout(function () { window.close(); }, " + timeout + ");<\/script>');");
}
window.alert("before sleep ...");
sleep(2000);
window.alert("after sleep ...");

缺點不用多說,只有IE支持(IE7因為安全限制也而不能達到目的)。

除上之外,還有利用Applet或者調用Windows Script Host的WScript.Sleep()等等鬼點子,這些都是萬不得已的權宜之計。

終於有了更聰明的人,開發出了也許是最佳的方案,先看代碼:

function sleep(millis)
{
    var notifier = NjsRuntime.createNotifier();
    setTimeout(notifier, millis);
    notifier.wait->();
} 

沒錯,看到 ->() 這樣的語法,就象剛看到Prototype的 $() 函數一樣讓我驚為天人。不過直接在瀏覽器中這段腳本是會報告語法錯誤的。實際上它們需要經過預編譯成客戶端瀏覽器認可的JavaScript。編譯后的腳本如下:

function sleep(millis)
{
    var njf1 = njen(this,arguments,"millis");
    nj:
    while(1) 
    {
        try
        {
            switch(njf1.cp) 
            {
                case 0:
                    njf1._notifier=NjsRuntime.createNotifier();
                    setTimeout(njf1._notifier,njf1._millis);
                    njf1.cp = 1;
                    njf1._notifier.wait(njf1);
                    return;
                case 1:
                    break nj;
            }
        }
        catch(ex)
        {
            if(!njf1.except(ex,1))
            return;
        }
    }
    njf1.pf();
}

我看不懂,也不想去看懂了。這些工作全部會由 Narrative JavaScript ———— 一個提供異步阻塞功能的JS擴展幫我們實現。我們只需要編寫之前那個怪異的 ->() 語法, 然后通過后台預先靜態編譯或者前台動態編譯后執行就可以實現 sleep 的效果。

Narrative JavaScript 宣稱可以讓你從頭昏眼花的回調函數中解脫出來,編寫清晰的Long Running Tasks。目前還是 alpha 的版本,在 Example 頁面上有一個移動的按鈕的范例。首頁上也提供了源碼下載。以我薄弱的基礎知識,我只能勉強的看出代碼中模擬了狀態機的實現,希望有精通算法的朋友能為我們解析。

最后,還是我一直以來的觀點: 除非很必要,否則請保持JavaScript的簡單。在JavaScript 能提供原生的線程支持之前,或許我們可以改變設計以避免異步阻塞的應用。

參考文章:

Agile Ajax - Narrative Javascript - Cleaner Code for Long Running Tasks
FAQTs - How do I pause execution in JavaScript?
==========有bug的曲折實現 

<script language="javascript"> 
/*Javascript中暫停功能的實現 
Javascript本身沒有暫停功能(sleep不能使用)同時 vbscript也不能使用doEvents,故編寫此函數實現此功能。 
javascript作為弱對象語言,一個函數也可以作為一個對象使用。 
比如: 
function Test()
{ 
    alert("hellow"); 
    this.NextStep=function()
    {
        alert("NextStep"); 
    } 
} 
我們可以這樣調用 var myTest=new Test();myTest.NextStep(); 

我們做暫停的時候可以吧一個函數分為兩部分,暫停操作前的不變,把要在暫停后執行的代碼放在this.NextStep中。 
為了控制暫停和繼續,我們需要編寫兩個函數來分別實現暫停和繼續功能。 
暫停函數如下: 
*/ 
function Pause(obj,iMinSecond)
{
    if (window.eventList==null)
        window.eventList=new Array(); 
    var ind=-1; 
    for (var i=0;i<window.eventList.length;i++)
    {
        if (window.eventList[i]==null)
        {
            window.eventList[i]=obj; 
            ind=i; 
            break; 
        } 
    }

    if (ind==-1)
    {
        ind=window.eventList.length; 
        window.eventList[ind]=obj; 
    }
    setTimeout("GoOn(" + ind + ")",1000); 
} 
/* 
該函數把要暫停的函數放到數組window.eventList里,同時通過setTimeout來調用繼續函數。 

繼續函數如下: 
*/ 
function GoOn(ind)
{
    var obj=window.eventList[ind]; 
    window.eventList[ind]=null; 
    if (obj.NextStep)
        obj.NextStep(); 
    else
        obj(); 
}
/* 
該函數調用被暫停的函數的NextStep方法,如果沒有這個方法則重新調用該函數。 

函數編寫完畢,我們可以作如下冊是: 
*/ 
function Test()
{
    alert("hellow"); 
    Pause(this,1000);//調用暫停函數
    this.NextStep=function()
    {
        alert("NextStep"); 
    }
} 
</script> 

 

Javascript順序執行的實現:
http://www.cnlei.org/blog/article.asp?id=297
JavaScript系列-同步還是異步:
http://blog.iecn.net/blog/html/do-showone-tid-966.html
Javascript中暫停功能的實現 :
http://blog.csdn.net/snakegod/archive/2004/09/22/112810.aspx
JavaScript Sleep函數 :
http://blog.csdn.net/gaooo/archive/2007/02/25/1514096.aspx
該文章轉載自腳本之家:http://www.jb51.net/html/200703/23/7505.htm
可暫停的滾動公告板
http://www.codebit.cn/pub/html/javascript/tip/pausing_up_down_scroller/

二、A函數調用B函數,B不僅能控制自身,也可以讓A來控制它

function funcA()
{ 
    funcB(); 
    //other code 
}

怎么定義函數B,讓B在運行的時候不僅能終止B本身,而且能終止函數A的運行?

這是個非常規的問題,我們分兩大部分討論. (1.為什么一定這樣做 2.怎么實現)

1. 顯然,這種編碼方式已經打亂了正規的程序編寫原則,我們編寫函數的目的就是為了封裝,為了實現代碼的模塊化. 如果B能讓A退出返回, 那這種編碼方式肯怕比濫用 goto 語句還濫了.

這樣做有必要嗎?為什么一定要這樣做....??

    答案如下:
假如我們要擴展Array的prototype.  比方說:定義一個  find方法,用來返回第一個讓 執行函數為真的數組元素.

<script> 
// by go_rush(阿舜) @ http://ashun.cnblogs.com 
    Array.prototype.each=function(f)
    { 
        for(var i=0;i<this.length;i++)
            f(this[i],i,this);
    }

    Array.prototype.find=function(f)
    {
        var result; 
        this.each(function(value,index,arr){if (f(value,index,arr))result=value;})
        return result;
    } 

    var arr=[1,2,3,4,5,7,9] 

    function foo(v)
    {
        //檢測是不是偶數 
         return v%2==0;
    } 
    alert(arr.find(foo));

</script> 


結果另我們大失所望.
首先: 在邏輯上,程序是錯誤的,因為我們期望返回第一個偶數,但是程序卻返回的是最后一個偶數.
其次: 程序的效率是低下的,那怕是找最后一個偶數,他在找到偶數4后,仍然檢測了4后面的所有元素.這個動作
是多余的. 

怎么辦呢? 請看代碼中的第11行,如果檢測到 f(value,index,arr)  為真的時候,能夠直接中斷函數 this.each()該多好啊.  效率,結果,雙贏的局面.

所以對於問題一 "為什么一定這樣做"  , 在這里,具體到這個應用上,有足夠的理由讓函數 B()來中斷函數A()

看到這里,你可能會問: 你的 find 方法為什么不這樣寫?

Array.prototype.find=function(f)
{   
    for(var i=0;i<this.length;i++)
    {
        if (f(this[i],i,this))
            return this[i];
    }
}


這樣不整個世界都清凈了嗎.

是的,如果我只是簡單的寫一個find 這樣寫肯定沒問題,但是如果現在我正在寫一個復雜的應用,或一個寫一個js框架呢

我要實現一系列的

Array.prototype.all 
Array.prototype.any 
Array.prototype.each 
Array.prototype.map 
Array.prototype.find 
Array.prototype.findAll 
Array.prototype.grep 
Array.prototype.inject 

......  詳細請參見 prototype.js v1.4 有上十種方法等着實現呢,我怎不可能每個方法都用 for循環一個一個的
遍歷數組把.  我肯定要實現一個 each 方法作為統一入口吧.

閑話少說,我們來看怎么解決問題:
要在 B函數中終止A函數,並返回結果, 目前我能想到的辦法就是用異常 try{}catch(x){}


實現代碼

<script> 
// by go_rush(阿舜) @ http://ashun.cnblogs.com 

var $break=new Object() 

Array.prototype.each=function(f){ 
    try{ 
    for(var i=0;i<this.length;i++){ 
        try{ 
             f(this[i],i,this) 
        }catch(e){ 
            if (e==$break) throw e 
        } 
    } 
    }catch(e){            
    } 
} 

Array.prototype.find=function(f){   
     var result; 
     this.each(function(value,index,arr){ 
         if (f(value,index,arr)){ 
             result=value 
            throw $break 
        }    
     }) 
     return result 
 } 

var arr=[1,2,3,4,5,7,9] 

function foo(v){    //檢測是不是偶數 
    return v%2==0 
} 
alert(arr.find(foo)) 

</script> 

 

在第24行,如果程序已經找到第一個滿足函數返回值為真的元素,那么就拋出一個自定義異常,終止 this.each()的
運行..   注意第12行,只有確保函數拋出的是自定義異常才繼續向上拋出異常,從而終止函數的運行.

在上面的代碼中,我用的 try---catch方法完全是用來解決本貼所提出的問題的,並未進行任何其他錯誤處理.

在這方面,prototype.js ,通過定義兩個自定義異常對象 $break 和 $continue ,既照顧到了異常處理,又解決了本貼
提出的問題. Enumerable 對象實現得很優雅, 大家不妨再去體會體會 prototype.js 中Enumerable的妙處.

我們看看prototype.js 是怎么做的,我還是貼出來把

prototype.js的代碼片段摘取

var $break    = new Object(); 
var $continue = new Object(); 

var Enumerable = { 
  each: function(iterator) { 
    var index = 0; 
    try { 
      this._each(function(value) { 
        try { 
          iterator(value, index++); 
        } catch (e) { 
          if (e != $continue) throw e; 
        } 
      }); 
    } catch (e) { 
      if (e != $break) throw e; 
    } 
  }, 

  all: function(iterator) { 
    var result = true; 
    this.each(function(value, index) { 
      result = result && !!(iterator || Prototype.K)(value, index); 
      if (!result) throw $break; 
    }); 
    return result; 
  }, 

  any: function(iterator) { 
    var result = true; 
    this.each(function(value, index) { 
      if (result = !!(iterator || Prototype.K)(value, index)) 
        throw $break; 
    }); 
    return result; 
  }, 

原文鏈接:http://harrison2010.iteye.com/blog/181550


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM