Можно ли выйти из рекурсивной функции в javascript через действие вне этой функции?

1

У меня есть сценарий (упрощенный в примере ниже), где действие пользователя инициирует рекурсивную функцию.

Пользователь может инициировать новое действие в то время, когда уже начатые действия еще не закончены.

Но я хотел бы гарантировать, что любое новое действие прекратит все предыдущие действия.

То, что на самом деле происходит в настоящее время (вы можете видеть это в приведенном ниже примере), заключается в том, что независимо от количества новых действий, инициируемых пользователем, каждое из ранее инициированных действий продолжается до тех пор, пока оно не закончится.

Я хотел бы знать, можно ли (в приведенном ниже примере) начать выращивать новый список, когда предыдущий список вырос только до 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>
Теги:
function
exit

2 ответа

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>
  • 0
    Спасибо, Михал. Я выбрал решение, которое не совсем совпадает с вашим выше, но следует тому же принципу в том, что оно передает внутренней функции идентификационный номер, который затем многократно сравнивается с переменной вне внутренней функции, которая может иметь был обновлен в то же время.
3

Вы можете использовать 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: стек вызовов пуст между таких звонков.

Это была настоящая (синхронная) рекурсия, не было бы ничего, что вы могли бы сделать с событиями кликов, поскольку эти вызовы вызова не будут вызываться до тех пор, пока не закончится рекурсия.

  • 0
    Большое спасибо за это, @trincot. Прежде чем обратиться за помощью к StackOverflow, я попробовал несколько подходов, использующих setTimeout и clearTimeout , но не смог заставить их работать успешно в случае моей (довольно сложной) установки. Тем не менее, я признаю, что это был бы нормальный способ приблизиться к прекращению повторяющихся процессов на ранней стадии.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню