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

你可能感兴趣的:(sleep)