`
hyw520110
  • 浏览: 221195 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

JS中的sleep 、顺序执行

    博客分类:
  • js
阅读更多
最近升级BLOG,需要在JS里实现暂停功能,还是小有些扰人,总达不到预期的效果,要么是将函数拆分为几个部分,要么采用事件机制,其实单线程是没有多线程的sleep功能,所以也只能这样过程Check了!
除了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方法,用来返回第一个让 执行函数为真的数组元素.

1 <script>
2 // by go_rush(阿舜) @ http://ashun.cnblogs.com
3
4 Array.prototype.each=function(f){
5     for(var i=0;i<this.length;i++) f(this[i],i,this)
6 }
7
8 Array.prototype.find=function(f){  
9     var result;
10     this.each(function(value,index,arr){
11         if (f(value,index,arr)) result=value
12     })
13     return result
14 }
15
16 var arr=[1,2,3,4,5,7,9]
17
18 function foo(v){    //检测是不是偶数
19     return v%2==0
20 }
21 alert(arr.find(foo))
22
23 </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){}


实现代码
1 <script>
2 // by go_rush(阿舜) @ http://ashun.cnblogs.com
3
4 var $break=new Object()
5
6 Array.prototype.each=function(f){
7     try{
8     for(var i=0;i<this.length;i++){
9         try{
10              f(this[i],i,this)
11         }catch(e){
12             if (e==$break) throw e
13         }
14     }
15     }catch(e){           
16     }
17 }
18
19 Array.prototype.find=function(f){  
20      var result;
21      this.each(function(value,index,arr){
22          if (f(value,index,arr)){
23              result=value
24             throw $break
25         }   
26      })
27      return result
28  }
29
30 var arr=[1,2,3,4,5,7,9]
31
32 function foo(v){    //检测是不是偶数
33     return v%2==0
34 }
35 alert(arr.find(foo))
36
37 </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;
  },
分享到:
评论

相关推荐

    javascript 使用sleep函数的常见方法详解

    但在JavaScript的单线程模型中,直接使用`sleep`会导致整个浏览器或Node.js应用变得无响应,因为它会阻止事件循环处理其他任务,包括用户交互和UI更新。 二、为何需要`sleep`函数? 在JavaScript中,通常使用`...

    前端开源库-try-thread-sleep

    在前端开发中,由于JavaScript是单线程的,开发者通常无法直接使用类似于后端编程中的线程睡眠(Thread.sleep)功能。线程睡眠是让当前执行的线程暂停指定时间,然后继续执行的一种方法,这对于某些场景如定时任务、...

    执行一个操作2秒后执行另一个操作.rar

    例如,`JavaScript`中的`setTimeout`和`setInterval`函数,Python的`time.sleep`或`threading.Timer`,Java的`java.util.Timer`类等。在"执行一个操作2秒后执行另一个操作"的例子中,可以使用定时器来设置2秒的时间...

    javascript里模拟sleep(两种实现方式)

    这种方式不会阻塞主线程,而是将任务放入事件队列,等到当前任务执行完毕后,再按顺序执行定时器中的任务。 ```javascript function sleep(ms) { return new Promise(resolve =&gt; setTimeout(resolve, ms)); } // ...

    js代码-简单的异步sleep

    "简单的异步sleep"这个主题涉及到如何在JavaScript中模拟一个延迟效果,使得代码在指定的时间间隔后继续执行。在实际开发中,这通常用于模拟等待或确保某些操作按特定顺序进行。 `main.js`可能是实现这个功能的核心...

    php和js编程中的延迟执行效果的代码

    ### PHP与JavaScript中的延迟执行技巧详解 ...而在JavaScript中,`setTimeout()`适合一次性延迟执行,而`setInterval()`则适用于周期性的定时任务。了解并掌握这些技术对于提高代码效率和程序性能具有重要意义。

    js模拟汉诺塔动画

    在这个过程中,JavaScript的回调函数用于控制代码的执行顺序,确保在正确的时间移动盘子。例如,我们可以定义一个名为`moveTower`的函数,接受三个参数:源柱子、目标柱子和辅助柱子。在函数内部,我们递归地调用...

    参考:关于Javascript中实现暂停的几篇文章

    - **具体实践**:在《[Javascript顺序执行的实现](http://www.cnlei.org/blog/article.asp?id=297)》这篇文章中,作者详细介绍了如何利用JavaScript的单线程特性来控制代码的执行顺序。例如,通过循环和递归等方式来...

    javascript如何实现暂停功能

    JavaScript通常运行在浏览器中,浏览器中有一个事件循环机制,所有的代码都是在事件循环中按顺序执行的。这意味着如果在当前函数中使用了阻塞操作,那么整个事件循环就会被阻塞,用户界面无法更新,这会导致浏览器无...

    js下写一个事件队列操作函数

    它检查`staticQueue`是否为空,如果不为空,则按顺序执行队列中的事件。对于异步事件(标记为`"async"`),它会执行对应的函数并使事件序列暂停(`sleep`),等待唤醒(`wake`)。同步事件则直接执行。 5. `sleep`和`...

    js基于setTimeout与setInterval实现多线程

    在JavaScript编程中,"多线程"这一概念实际上是基于事件驱动和异步执行的单线程模型来模拟实现的。JavaScript的单线程特性意味着在浏览器的主线程中,任何时候只有一个任务可以被执行。然而,为了实现异步操作,比如...

    飞流直下,瀑布效果

    2. **使用sleep()方法**:在每个线程的执行逻辑中,使用`sleep()`方法来设定线程暂停的时间。这个时间值决定了瀑布的水流速度,较短的延迟会让水流看起来更快,较长的延迟则更慢。 3. **绘制瀑布**:线程恢复执行后...

    【windows 脚本系列】13. 从Script中控制Windows和应用程序

    通过使用Windows Script Host中的`Sleep`方法,开发者可以有效地管理脚本执行过程中的一些常见问题,如等待条件变化、减轻CPU负担以及解决异步处理带来的挑战。无论是对于初级程序员还是经验丰富的开发人员来说,...

    理解javascript定时器中的单线程

    由于`sleep`函数实际上阻塞了JavaScript引擎,因此在这个过程中,任何其他事件(如定时器)都不会执行,直到`sleep`函数完成。 总结,理解JavaScript的单线程特性和事件循环机制是编写高性能、响应式Web应用的关键...

    06-手写lazyman.md

    4. 依次执行队列中的任务,根据`sleep`方法中的延时来控制任务的执行顺序。 示例代码框架如下: ```js class LazyMan { private name: string; private taskQueue: Array&lt;() =&gt; void&gt; = []; constructor(name:...

    python如何实现异步调用函数执行

    - **同步调用**:在同步调用模式下,程序执行流程是按顺序进行的。当前一个操作没有完成时,程序会等待该操作完成后才会继续执行后续的操作。这种模式适用于操作简单且耗时较短的情况。 - **异步调用**:异步调用则...

    2012年计算机二级Java线程例子学习教程.pdf

    在7.2.1节中,讲解了Java如何自动创建和管理线程,通常在浏览器中,如JavaScript小程序的执行就是多线程的体现。多线程程序通过将任务分解为多个独立的子任务,每个线程负责一个子任务,这样可以使得程序更加高效,...

Global site tag (gtag.js) - Google Analytics