简体   繁体   English

JavaScript中的睡眠功能-不使用递归

[英]Sleep function in javascript - without using recursion

First of all, I've had a look on all the 'sleep' questions lying around (such as What is the JavaScript version of sleep()? ) but I didn't find an acceptable solution. 首先,我研究了周围所有的“睡眠”问题(例如,sleep()的JavaScript版本是什么? ),但没有找到可接受的解决方案。

I would like to make a visual education tool for all sort of algorithms. 我想为各种算法制作一个可视化的教育工具。 In order to do so, I'm using javascript with jQuery to display the data and paint it up nicely. 为了做到这一点,我在jQuery中使用javascript来显示数据并将其很好地绘制。 In order to start it up, I want to do a sorting sample, where an array is displayed, shuffled and then sorted in a visually pleasing way. 为了启动它,我想做一个排序示例,在其中显示一个数组,将其改组后再以视觉上令人愉悦的方式进行排序。 So what I want to happen is that two cells get highlighted (easy), possibly swapped (easy), and then there's a small delay before the next pair is tested (hard). 所以我想发生的是,两个单元格被突出显示(容易),可能被交换(容易),然后在测试下一个对(困难)之前有一个小的延迟。

I understand there isn't an explicit 'sleep' method in javascript. 我了解到javascript中没有明确的“睡眠”方法。 However, to restructure the code into using setTimeout would imply rewriting all my algorithms recursively, which is a huge hinder (although obviously not impossible). 但是,将代码重组为使用setTimeout意味着将递归重写所有算法,这是一个巨大的障碍(尽管显然并非不可能)。

As a sample problem, take a look at a bubble sort sample: 作为一个示例问题,请看一下气泡排序示例:

function bubble_sort(arr){
    for(var i=0;i<arr.length;i++){
        for(var j=1;j<arr.length;j++){
            highlight(j-1);
            highlight(j);
            if(arr[j-1] > arr[j]){
                visible_swap(arr, j, j-1);
            }
            sleep(1000);
        }
    }
    exhibit_array(arr);
}

This can obviously rewritten recursively to work with setTimeout, but to do so on all the algorithms I have in mind would take a great deal of time. 显然,这可以递归地重写以与setTimeout一起使用,但是要对我所考虑的所有算法进行此操作都将花费大量时间。 Am I missing something? 我想念什么吗? Is there an 'easy' way to leave the implementations as they are and place sleeps at will? 有没有一种“简便”的方法可以使实现保持现状并随意休眠?

EDIT: I found two solutions: a pretty one, and a compatible one. 编辑:我发现了两个解决方案:一个漂亮的和一个兼容的。 The pretty one only works on firefox, I'm afraid, and makes use of the wonderful yield semantics (There is some sample explanation here: https://developer.mozilla.org/en/New_in_JavaScript_1.7 ). 恐怕,漂亮的代码只能在firefox上使用,并利用了出色的yield语义(此处有一些示例说明: https : //developer.mozilla.org/en/New_in_JavaScript_1.7 )。 This actually solves my problem perfectly, thus: 这实际上完美地解决了我的问题,因此:

function bubble_sort(arr){
    for(var i=0;i<arr.length;i++){
        for(var j=1;j<arr.length;j++){
            highlight(j-1);
            highlight(j);
            if(arr[j-1] > arr[j]){
                visible_swap(arr, j, j-1);
            }
            yield true;
        }
    }
    yield false;
}
var bubble = bubble_sort(arr)
function gen(){
    if(bubble.next()){
        setTimeout(gen, 500);
    }
    else{
        alert("Done!");
    }
}

This works wonderfully for me, but does rely on the yield capability which currently is only supported on firefox. 这对我来说效果很好,但确实依赖于当前仅在firefox中支持的yield功能。 Notice that for this to work at all, you need to use <script type="text/javascript;version=1.7">. 请注意,要使其完全起作用,您需要使用<script type =“ text / javascript; version = 1.7”>。 This however is perfect. 然而,这是完美的。 It could have also worked for infinite algorithms, showing them toiling in vain if need be. 它也可能适用于无限算法,如果需要的话,显示它们徒劳无功。

The second solution I found works as well, based on the answer below: 根据以下答案,我发现的第二个解决方案也有效:

function bubble_sort(arr){
    var q = new Array();
    for(var i=0;i<arr.length;i++){
        for(var j=1;j<arr.length;j++){
            q[q.length] = [ [highlight, j-1 ], [highlight, j] ];
            if(arr[j-1] > arr[j]){
                swap(arr, j, j-1);
                q[q.length] = [ [visible_swap, j, j-1] ];
            }
        }
    }
    return q;
}
function play(q, step){
    if(q.length == 0)
        return;
    clear_highlights();
    cmds = q.shift();

    for(ind in cmds){
        cmd = cmds[ind];
        f = cmd.shift();
        f.apply(null, cmd);
    }
    setTimeout(function(){ play(q, step); }, step);
}

This works as well. 这也可以。 This is pretty bothersome syntactically, but definitely works well on all browsers. 从语法上来说这很麻烦,但是绝对可以在所有浏览器上正常工作。

After all this though, it seems there are javascript 'extensions' which implement sleep-like syntax, which is obviously better than all of the above. 尽管如此,似乎所有的javascript“扩展”都实现了类似于睡眠的语法,这显然比上述所有方法都要好。 Thanks for the help! 谢谢您的帮助!

Recently I made a visualization of sub-palindrome finder algorithm, it used setTimeout and didn't require rewriting of the algorithm in recursive form. 最近,我对次回文查找器算法进行了可视化处理,它使用了setTimeout,并且不需要以递归形式重写算法。

See this example . 请参阅此示例

The general principle is to build up a stack of commands, for bubble sort that would be a stack of highlight and swap commands. 一般原则是为气泡排序建立一堆命令,这将是一串高亮和交换命令。 Then you can have a function running each N milliseconds which takes a command from the stack and visualizes it. 然后,您可以使函数每N毫秒运行一次,该函数从堆栈中获取命令并将其可视化。

commands = [
    ['highlight', 1, 5]
    ['swap', 1, 5]
    ['highlight', 3, 7]
    ...
];

setInterval(function() {
    var cmd = commands.shift();
    visualize(cmd);
}, 1300);

In my problem the finder algorithm was written in Python and was provided by the user, and I couldn't modify it. 在我的问题中,finder算法是用Python编写的,由用户提供,我无法对其进行修改。 Fortunately Python allows to overload access and comparison operators and record each action the algorithm takes. 幸运的是,Python允许重载访问和比较运算符,并记录算法执行的每个动作。 RecString class . RecString类 In JavaScript you can't do that, but that's not a problem in your case, because you can modify the original algorithm. 在JavaScript中,您不能这样做,但这对您来说不是问题,因为您可以修改原始算法。

I can email you the JS source if you want, it was written in haste, but might be useful anyway. 我可以通过电子邮件将JS来源通过电子邮件发送给您,它是匆忙编写的,但仍然很有用。

Another idea - StratifiedJS . 另一个想法-StratifiedJS Here's a simple jsFiddle example : 这是一个简单的jsFiddle示例

<script src="http://code.onilabs.com/apollo/0.13/oni-apollo.js"></script>
<script type="text/sjs">
  for (var i = 1; i < 4; i++) {
      alert(i);
      hold(1000);
  }
</script>

我将使用setTimeout ,我相信这是您在客户端上最接近“睡眠”状态的方法。

This answer doesn't solve the general case, but perhaps you can increment the interval for each instruction so that they run one second after each other. 这个答案不能解决一般情况,但是也许您可以增加每条指令的间隔,以使它们彼此之间每秒运行一秒钟。

function bubble_sort(arr){
    var interval = 0;  // increases with each loop
    for(var i=0;i<arr.length;i++){
        for(var j=1;j<arr.length;j++){
            (function(i, j) {
                setTimeout(function() {
                    highlight(j-1);
                    highlight(j);
                    if(arr[j-1] > arr[j]){
                        visible_swap(arr, j, j-1);
                    }
                }, interval);
            })(i, j);
            interval += 1000;
        }
    }
    exhibit_array(arr);
}

Thus, the first operation runs at once, the next runs after one second, the thrid after a total of two seconds, etc. 因此,第一个操作一次运行,第二个运行在一秒钟后运行,第三个运行总计两秒钟后,依此类推。

This solution provides the benefit of minimal code rewriting: just wrap your loop contents in a setTimeout (which is wrapped inside a closure with your loop variables) and add a line to increment interval after each loop iteration. 该解决方案提供了最小化代码重写的优势:只需将循环内容包装在setTimeout (该包装与循环变量一起包装在闭包中),并在每次循环迭代后添加一行以增加interval

Using setTimeout() is not recursion. 使用setTimeout()不是递归的。

You can work with a closure to keep track of state. 您可以使用闭包来跟踪状态。 The for loops, however, have to be changed into while for this to work: 但是,必须将此for循环更改为while才能起作用:

function bubbleSort(arr) {
  (function(i, j) { // <- this closes over i and j
    function nextSortStep() {
      while (i < arr.length) {
        while (j < arr.length) {
          highlight(j - 1);
          highlight(j);
          if (arr[j - 1] > arr[j]) {
            visibleSwap(arr, j, j - 1);
          }
          j++;
          return setTimeout(nextSortStep, 1000);
        }
        i++;
        j = 1;
        return setTimeout(nextSortStep, 1000);
      }
      exhibitArray(arr);
    }
    nextSortStep();
  })(0, 1); // <- loop initialization
}

As an aside, JavaScript is not PHP, function names generally are in camelCase . camelCase ,JavaScript不是PHP,函数名称通常在camelCase

Following Lebedev's idea, I would store the "evolution of the sorting of the array" and then use setInterval() to show them. 按照Lebedev的想法,我将存储“数组排序的演变”,然后使用setInterval()进行显示。 http://jsfiddle.net/mari/EaYRZ/ http://jsfiddle.net/mari/EaYRZ/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM