【问题标题】:Is it possible to exit from a recursive function in javascript via an action outside of that function?是否可以通过该函数之外的操作退出 javascript 中的递归函数?
【发布时间】:2018-02-11 21:39:52
【问题描述】:

我有一个脚本(在下面的示例中进行了简化),其中用户操作会启动递归函数。

用户可以在已经启动的操作尚未完成运行时启动新操作。

但我想确保任何新操作停止所有之前的操作

目前实际发生的情况(您可以在下面的示例中看到这一点)是,无论用户启动了多少新操作,之前启动的每个操作都会继续执行,直到完成。

我想知道(在下面的示例中)是否可以在前一个列表仅增长到 5 个项目时开始增长一个新列表,并确保前一个列表不再增长。

var trigger = document.getElementsByTagName('p')[0];
var o = 0;

function addListItems(orderedList, listItemNumber) {

    var listItems = orderedList.getElementsByTagName('li');
    
    if (listItemNumber > 9) {
        return;
    }
    
    listItems[listItemNumber].classList.add('show');
    listItemNumber++;
    setTimeout(function(){addListItems(orderedList, listItemNumber)}, 400);
}

function startRecursiveFunction() {
    
    var orderedList = document.getElementsByTagName('ol')[o];
    var listItemNumber = 0;
    if (o < 7) {
        addListItems(orderedList, listItemNumber);
    }
    o++;
}

trigger.addEventListener('click', startRecursiveFunction, false);
p {
font-weight: bold;
cursor: pointer;
}

ol {
display: inline-block;
}

li {
opacity: 0;
}

li.show {
opacity: 1;
}
<p>Click to trigger function (up to 7 times)</p>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ol>

【问题讨论】:

    标签: javascript function exit


    【解决方案1】:

    您可以使用clearTimeout 取消已安排的超时事件。

    注意代码中标有*** 的三处更改,它们都处理新的timer 变量:

    var trigger = document.getElementsByTagName('p')[0];
    var o = 0;
    var timer; //***
    
    function addListItems(orderedList, listItemNumber) {
    
        var listItems = orderedList.getElementsByTagName('li');
        
        if (listItemNumber > 9) {
            return;
        }
        
        listItems[listItemNumber].classList.add('show');
        listItemNumber++;
        // ***
        timer = setTimeout(function(){addListItems(orderedList, listItemNumber)}, 400);
    }
    
    function startRecursiveFunction() {
        
        var orderedList = document.getElementsByTagName('ol')[o];
        var listItemNumber = 0;
        clearTimeout(timer); // ***
        if (o < 7) {
            addListItems(orderedList, listItemNumber);
        }
        o++;
    }
    
    trigger.addEventListener('click', startRecursiveFunction, false);
    p {
    font-weight: bold;
    cursor: pointer;
    }
    
    ol {
    display: inline-block;
    }
    
    li {
    opacity: 0;
    }
    
    li.show {
    opacity: 1;
    }
    <p>Click to trigger function (up to 7 times)</p>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>

    但是,我应该补充一点,这不是真正的递归:调用堆栈不会随着每次调用 addListItems 而不断增长,因为当通过 setTimeout 事件启动下一次调用时,上一次调用已经结束: 在这样的调用之间调用堆栈是空的。

    如果是真正的(同步)递归,您将无法处理点击事件,因为在递归完成之前不会调用这些事件回调。

    【讨论】:

    • 非常感谢@trincot。在来 StackOverflow 寻求帮助之前,我尝试了多种使用 setTimeoutclearTimeout 的方法,但在我的(相当复杂的)设置的情况下无法成功工作。尽管如此,我认识到这将是一种提前终止重复流程的正常方法。
    【解决方案2】:

    您可以使用计数器变量,每次调用startRecursiveFunction 时都会增加该变量。在setTimeout 回调中,您检查计数器是否已更改,如果是,则停止递归。

    var trigger = document.getElementsByTagName('p')[0];
    var o = 0;
    var fnId = 0;
    
    function addListItems(orderedList, listItemNumber) {
    
        var listItems = orderedList.getElementsByTagName('li');
        
        if (listItemNumber > 9) {
            return;
        }
        
        listItems[listItemNumber].classList.add('show');
        listItemNumber++;
    
        var lastFnId = fnId;
        setTimeout(function() {
          if (lastFnId === fnId) {
            addListItems(orderedList, listItemNumber);
          }
        }, 400);
    }
    
    function startRecursiveFunction() {
        
        var orderedList = document.getElementsByTagName('ol')[o];
        var listItemNumber = 0;
        if (o < 7) {
            fnId++;
            addListItems(orderedList, listItemNumber);
        }
        o++;
    }
    
    trigger.addEventListener('click', startRecursiveFunction, false);
    p {
    font-weight: bold;
    cursor: pointer;
    }
    
    ol {
    display: inline-block;
    }
    
    li {
    opacity: 0;
    }
    
    li.show {
    opacity: 1;
    }
    <p>Click to trigger function</p>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>
    
    <ol>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    </ol>

    【讨论】:

    • 谢谢 Michal - 我采用的解决方案与您上面的解决方案不完全相同,但遵循相同的原则,即它传递给内部函数的 ID 号,然后反复与内部函数之外的变量,同时可能已更新。
    猜你喜欢
    • 1970-01-01
    • 2018-07-29
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 1970-01-01
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多