如何在 js 中循环遍历字符串列表并同时降低加载速度?

How do I loop trough a list of strings and decrease the looping speed at the same time in js?

我想创建一个 javascript 循环遍历所有名称并始终在 UI (html) 中显示循环的当前名称。但同时循环速度应该降低。

假设我们以每秒 10 次循环开始。然后它应该减少直到它的 1 个循环/秒,然后在那个点停止并显示名称。这是一种抽奖活动,获胜者应该是随机的。

所以我创建了下面的脚本,但问题是没有任何反应?不知何故,什么也没有发生。它执行代码但没有错误,UI 中也没有任何变化。 代码确实得到执行(通过登录到控制台对其进行测试)!

我的函数:

function Roll(){
    var delay = 100;
    for(var i = 0; delay < 1000; i++){
        delay += 25;
        if(i >= listOfParticipants.length){
            i = 0;
        }
        setTimeout(function() {
            document.getElementById("person_name").value = listOfParticipants[i];
        }, delay);
    }
}

html 元素:

<h1 class="text-center mt-2" id="person_name" ></h1>

编辑: 我想我知道问题是什么,但我不知道如何解决。我认为 for 循环不会等待 setTimeout 完成。不知道怎么处理。

编辑 2: setTimeout 似乎创建了一个新的 ExecutionContext。这意味着 for 循环结束并在执行 setTimeout 之前获得其最大值 (13)。现在的问题是如何在当前的 ExecutionContext 中添加超时? 列表只有 12 个对象,这就是 i = 13 的结果未定义的原因。

假设您想停止在数组的姓氏上(因为在您的问题中不清楚名称选择应该如何工作),您可以使用一种策略。

基本上,您想要循环遍历listOfParticipants 数组,而不是循环遍历持续时间。在每次迭代中,我们都会减少在继续 for 循环的下一次迭代之前等待的持续时间。

// Pseudo code!
async function roll() {
  for (let i = 0; i < listOfParticipants.length; i++) {
    // Wait for promise to resolve
    await someKindOfPromise();

    // Update name
    document.getElementById('person_name').innerText = listOfParticipants[i];
  }
}

这可以使用 async/await 来完成。 ES6 中的原生 for 循环支持等待承诺,因此我们只需创建一个方法,该方法 returns 一个在 x 秒后解析的承诺:

// A simple promise that resolves after a specified duration
function wait(duration) {
  return new Promise(resolve => {
    window.setTimeout(resolve, duration);
  });
}

经过 x 秒后,我们进入下一次迭代。

x 的值可以简单地通过根据我们在参与者数组中的相对位置递减预设持续时间来确定。您在数组中越远(由索引 i 表示),您希望时间越短,即:delay * i / listOfParticipants.length;

async function roll() {
  const delay = 100;
  for (let i = 0; i < listOfParticipants.length; i++) {
    // Force the for loop to wait for a set amount of time
    const duration = delay * i / listOfParticipants.length;
    await wait(duration);

    // Once the duration has been awaited, we can then update the inner text
    document.getElementById('person_name').innerText = listOfParticipants[i];
  }
}

请参阅下面的概念验证:

const listOfParticipants = ['Adena', 'Socorro', 'Germaine', 'Ebony', 'Raul', 'Anton', 'Rochel', 'Morgan', 'Joanie', 'Ellsworth', 'Edelmira', 'Susannah', 'Gino', 'Vicenta', 'Katrina', 'Devorah', 'Olinda', 'Lise', 'Napoleon', 'Dessie', 'Herta', 'Cassaundra', 'Nadine', 'Dalton', 'Mica', 'Haydee', 'Linh', 'Williemae', 'Desiree', 'Philomena', 'Julio', 'Darell', 'Shana', 'Ligia', 'Melita', 'Laurene', 'Darby', 'Gregg', 'Shemika', 'Tesha', 'Benita', 'Hyman', 'Kattie', 'Mary', 'Julienne', 'Claud', 'Heather', 'Toney', 'Vasiliki', 'Stephani', 'Violette', 'Barney', 'Warren', 'Felix', 'Mathew', 'Blair', 'Jamar', 'Grover', 'Bud', 'Barbie', 'Gina'];

// A simple promise that resolves after a specified duration
function wait(duration) {
  return new Promise(resolve => {
    window.setTimeout(resolve, duration);
  });
}

async function roll() {
  const delay = 100;
  for (let i = 0; i < listOfParticipants.length; i++) {
    // Force the for loop to wait for a set amount of time
    const duration = delay * i / listOfParticipants.length;
    await wait(duration);
    
    // Once the duration has been awaited, we can then update the inner text
    document.getElementById('person_name').innerText = listOfParticipants[i];
  }
}

document.querySelector('#btn').addEventListener('click', () => roll());
<h1 class="text-center mt-2" id="person_name"></h1>
<button id="btn">Raffle</button>

您可以递归调用 setTimeout() 而不是使用循环:

<h1 class="text-center mt-2" id="person_name" ></h1>
var listOfParticipants = [
  "Kurt",
  "Dave",
  "Krist",
  "Alanis",
  "Aimee",
  "Chris",
  "Eddie",
  "Mike",
  "Stone",
  "Jeff",
  "Matt",
  "Boom"
]

function doIt(i) {
  var participant = listOfParticipants[i];
  if (!participant) return;

  document.getElementById("person_name").innerHTML = participant;
  setTimeout(
    function() { 
      doIt(i + 1);
    }, 
    100 + i * 100
  );
}

doIt(0);