javascript - 嵌套循环输出的时间

标签 javascript while-loop

我刚开始使用JavaScript,并且试图编写一个用于指板训练的小型开源应用程序。我嵌套了一些时间循环以说明hangtime和break之间的间隔:

<html>

<body>
  Sets: <input type="number" id="setsIN" value="5">
  <br>
  Rounds: <input type="number" id="roundsIN" value="6">
  <br>
  Workout: <input type="number" id="hangIN" value="7">
  <br>
  Short Break: <input type="number" id="shortbreakIN" value="3">
  <br>
  Long Break: <input type="number" id="longbreakIN" value="180">
  <br>

  <hr>

  <script>
    // Import values
    var setsNUMBER = parseInt(document.getElementById("setsIN").value);
    var roundsNUMBER = parseInt(document.getElementById("roundsIN").value);
    var hangTIME = parseInt(document.getElementById("hangIN").value);
    var shortbreakTIME = parseInt(document.getElementById("shortbreakIN").value);
    var longbreakTIME = parseInt(document.getElementById("longbreakIN").value);
    console.log("Sets: " + setsNUMBER)
    console.log("Rounds: " + roundsNUMBER)
    console.log("Hang: " + hangTIME)
    console.log("Short breaks: " + shortbreakTIME)
    console.log("Long breaks: " + longbreakTIME)
    // calculate duration
    var duration = ((hangTIME + shortbreakTIME) * roundsNUMBER + longbreakTIME) * setsNUMBER
    console.log("Duration (minutes): " + duration/60)
    // Counter
    var setsCOUNT = 1; // Start counting at 1
    var roundsCOUNT = 1; // Start counting at 1
    var hangCOUNT = 1; // Start counting at 1
    var shortbreakCOUNT = 1; // Start counting at 1
    var longbreakCOUNT = 1; // Start counting at 1
/////////////////////////////////////////////////////////////////

  // Sets
  while (setsCOUNT < setsNUMBER+1) {
    console.log("Set: "+ setsCOUNT)
    setsCOUNT++;
    roundsCOUNT = 1;
    longbreakCOUNT = 1;
    // Rounds
      while (roundsCOUNT  < roundsNUMBER+1) {
        console.log("Round: "+ roundsCOUNT)
        roundsCOUNT++;
        hangCOUNT = 1;
        shortbreakCOUNT = 1;
        // HAngtime
          while (hangCOUNT  < hangTIME+1) {
            console.log("WorkOutTime: "+ hangCOUNT)
            hangCOUNT++;
          }
        // Pausetime
         while (shortbreakCOUNT  < shortbreakTIME+1) {
           console.log("ShortBreak: "+ shortbreakCOUNT)
           shortbreakCOUNT++;
         }
          }
          // LongBreak
           while (longbreakCOUNT  < longbreakTIME+1) {
             //console.log("longBreak: "+ longbreakCOUNT)
             longbreakCOUNT++;
      }
  }

  </script>

</html>


培训的顺序如下:
-7秒锻炼
-休息3秒
重复以上六次(= 60秒)
休息180秒
重复以上五次的所有步骤(= 5 * 4分钟)

看来我正确地输出了序列。 console.log()以正确的顺序返回。但是,当前,每当我运行脚本时,加载页面后立即返回所有日志行。如何每秒打印一行?我尝试了setTimeout(),但无法运行。

最佳答案

循环不应该用来组织时间。循环是一系列操作迅速发生的一系列操作。

我要做的是使用一些时间函数:setTimeout *或requestAnimationFrame,尽管在​​后者中,您必须自己跟踪每个帧中的时间。可行并且可能更可靠*,但是出于示例的原因,我使用了setTimeout。无论如何,这是概念的证明。

您想要做的是:


开始设置,然后致电“开始”或“结束”
开始一轮,然后致电开始锻炼或开始下一组
重复6次:


锻炼身体,然后在7秒后致电短暂休息或长时间休息
休息片刻,然后3秒钟后致电锻炼

稍事休息,然后在180秒后致电“开始设置”


这是一系列步骤,需要x倍的时间(新设置和新回合除外,它们只是数据更新),之间有一定的暂停时间。



不要空手而归,这是概念的有效证明:

<html>
<body>
  Sets: <input type="number" id="setsIN" value="5">
  <br>
  Rounds: <input type="number" id="roundsIN" value="6">
  <br>
  Workout: <input type="number" id="hangIN" value="7">
  <br>
  Short Break: <input type="number" id="shortbreakIN" value="3">
  <br>
  Long Break: <input type="number" id="longbreakIN" value="180">
  <br>

  <hr>

  <script>
    const setsNUMBER = 'setsNUMBER';
    const roundsNUMBER = 'roundsNUMBER';
    const hangNUMBER = 'hangNUMBER';
    const hangTIME = 'hangTIME';
    const shortbreakTIME = 'shortbreakTIME';
    const longbreakTIME = 'longbreakTIME';

    const setsCOUNT = 'setsCOUNT';
    const roundsCOUNT = 'roundsCOUNT';
    const hangCOUNT = 'hangCOUNT';
    const shortbreakCOUNT = 'shortbreakCOUNT';
    const longbreakCOUNT = 'longbreakCOUNT';

    function training({ config, data, next: current }) {
        switch (current) {
            case setsCOUNT: {
                if (data[setsCOUNT] < config[setsNUMBER]) {
                    const updatedSetsCOUNT = data[setsCOUNT] + 1;
                    console.log(`Set: ${updatedSetsCOUNT}`);

                    training({
                        config,
                        data: {
                            ...data,
                            [setsCOUNT]: updatedSetsCOUNT,
                            [roundsCOUNT]: 0,
                        },
                        next: roundsCOUNT,
                    });
                } else {
                    console.log('The end. It was a good workout, bro!');
                }
                break;
            }

            case roundsCOUNT: {
                if (data.roundsCOUNT < config.roundsNUMBER) {
                    const updatedRoundsCOUNT = data.roundsCOUNT + 1;
                    console.log(`Round: ${updatedRoundsCOUNT}`);

                    training({
                        config,
                        data: {
                            ...data,
                            [roundsCOUNT]: updatedRoundsCOUNT,
                            [hangCOUNT]: 0,
                            [shortbreakCOUNT]: 0,
                        },
                        next: hangCOUNT,
                    });
                } else {
                    console.log('New set');

                    training({
                        config,
                        data: {
                            ...data,
                            roundsCOUNT: 0,
                        },
                        next: setsCOUNT,
                    });
                }
                break;
            }

            case hangCOUNT: {
                if (data[hangCOUNT] < config[hangNUMBER]) {
                    const updatedHangCOUNT = data[hangCOUNT] + 1;
                    console.log(`WorkOutTime: ${updatedHangCOUNT}`);

                    setTimeout(training, config[hangTIME] * 1000, {
                        config,
                        data: {
                            ...data,
                            [hangCOUNT]: updatedHangCOUNT,
                        },
                        next: shortbreakTIME,
                    });
                } else {
                    training({
                        config,
                        data,
                        next: longbreakCOUNT,
                    });
                }
                break;
            }

            case shortbreakTIME: {
                const updatedShortBreakCOUNT = data[shortbreakCOUNT] + 1;
                console.log(`Short break: ${updatedShortBreakCOUNT}`);

                setTimeout(training, config[shortbreakTIME] * 1000, {
                    config,
                    data: {
                        ...data,
                        [shortbreakCOUNT]: updatedShortBreakCOUNT,
                    },
                    next: hangCOUNT,
                });
                break;
            }

            case longbreakCOUNT: {
                // this update is probably obsolete as setsCOUNT stage is keeping track
                const updatedLongbreakCOUNT = data[longbreakCOUNT] + 1;
                console.log(`LongBreak: ${updatedLongbreakCOUNT}`);

                setTimeout(training, config[longbreakTIME] * 1000, {
                    config,
                    data: {
                        ...data,
                        [longbreakCOUNT]: updatedLongbreakCOUNT,
                    },
                    next: roundsCOUNT,
                });
                break;
            }
        }
    }

    const config = {
        [setsNUMBER]: parseInt(document.getElementById("setsIN").value),
        [roundsNUMBER]: parseInt(document.getElementById("roundsIN").value),
        [hangNUMBER]: 6,
        [hangTIME]: parseInt(document.getElementById("hangIN").value),
        [shortbreakTIME]: parseInt(document.getElementById("shortbreakIN").value),
        [longbreakTIME]: parseInt(document.getElementById("longbreakIN").value),
    };

    console.log("Sets: " + config.setsNUMBER);
    console.log("Rounds: " + config.roundsNUMBER);
    console.log("Workout time: " + config.hangTIME);
    console.log("Short break time: " + config.shortbreakTIME);
    console.log("Long break time: " + config.longbreakTIME);
    console.log("Duration (minutes): " + (
        (
            (
                (config.hangTIME + config.shortbreakTIME)
                * config.roundsNUMBER
                + config.longbreakTIME
            )
            * config.setsNUMBER
        )
        / 60)
    );

    const data = {
        [setsCOUNT]: 0,
        [roundsCOUNT]: 0,
        [hangCOUNT]: 0,
        [shortbreakCOUNT]: 0,
        [longbreakCOUNT]: 0,
    };

    training({ config, data, next: setsCOUNT });
  </script>
</body>
</html>


一般概念是函数training,该函数接受带有配置,数据和下一步的任意对象,并再次使用新值调用自身,直到满足条件(在这种情况下,完成的集数)。

我建议不要按交付使用它。


我会将其拆分为一些小功能。
如果在短暂的休息之后出现了短暂的休息,那么可能会跳过这种短暂的休息。
上面的函数有时会变异对象。这不太可能引起问题,但通过时创建新对象可能会更安全。由你决定。


[编辑]工作演示:https://jsfiddle.net/hmcwf0p5/1/

[编辑]代码2

然后,有人问我上面的代码是否可以显示秒数。 (这是一个很好的示例,为什么从一开始就应该弄清楚规格;否则,可能会导致非常不同的实现。)

简而言之,没有。

长话短说,由于setTimeout的性质,我们可以做到这一点并不准确。我什至不想去。因此,我开始思考不同的方法,并提出了时间表。

代码:时间轴

想法:运行定时功能,在给定间隔内执行某些操作。在我们的情况下,这将是1秒。然后,根据我们的数据,我们应该做一些事情。

const prepareTimeline = (possibleTimeline) => {
    const sortedFrames = Object.keys(possibleTimeline)
        .map(Number)
        .filter((number) => !Number.isNaN(number))
        .sort((a, b) => a - b);

    return Object.assign(
        sortedFrames.reduce(
            (acc, number) => Object.assign(acc, { [number]: possibleTimeline[number] }),
            {}
        ),
        { last: Math.max(...sortedFrames) + 1 }
    );
}

const handleFrame = (data) => {
    const { second, frames, frames: { last }, timelineId } = data;

    if (second == last) {
        return clearInterval(timelineId);
    }

    if (frames[second]) {
        frames[second].forEach((message) => {
            console.log(message);
        });
    }

    data.second = second + 1;
};

const runTimeline = (frames) => {
    const timelineObject = {
        second: 0,
        frames: prepareTimeline(frames),
        timelineId: null,
    }

    const timelineId = setInterval(handleFrame, 1000, timelineObject);
    timelineObject.timelineId = timelineId;
}


怎么了:


runTimeline拍摄带有框架的对象。关键是发生某事的秒数,值是要显示消息的数组。
prepareTimeline函数内部,删除了所有非数字键(作为frames属性传递),并添加了last,这是最大值加上1秒,因此我们可以杀死setInterval
handleFrame是每秒调用的函数。作为参数,它使用timelineId(可以传递给clearInterval的对象),framessecond作为对象。在此函数内部,我们对该对象进行了突变。


在这里,您可以决定要对框架做什么。您可以使用应用程序的setState或任何您认为合适的状态来代替console.logging它。
另外,在我的示例中,它是数组的对象,但要取决于handleFrame接受什么。



符号setInterval(functionName, time, ...params)将每秒调用一次functionName(...params)

现在,运行它:

runTimeline({
    1: ['Initial message'],
    5: ['Second message', 'And last'],
});


在控制台日志中,1秒后首先显示消息

Initial message


然后4秒钟后(同时):

Second message
And last


锻炼时间表

所以剩下的就是建立锻炼框架。 buildWorkoutTimeline接受参数并运行许多嵌套循环(这在某种程度上是最初的实现)。可能的书写方式可能有所不同,但我会留给您。

const buildWorkoutTimeline = ({
    sets = 3,
    rounds = 5,
    workouts = 6,
    workoutTime = 7,
    pauseTime = 3,
    restTime = 180,
} = {}) => {
    let seconds = 0;
    const workoutTimeline = { [seconds]: [] };

    for (let set = 1; set <= sets; set++) {
        workoutTimeline[seconds].push(`New set: ${set}`);
        for (let round = 1; round <= rounds; round++) {
            workoutTimeline[seconds].push(`Set ${set}’s new round: ${round}`);
            for (let workout = 1; workout <= workouts; workout++) {
                seconds += 1;
                workoutTimeline[seconds] = [`Set ${set}, round ${round}’s new workout: ${workout}`];
                for (let time = 0; time < workoutTime; time++) {
                    seconds += 1;
                    workoutTimeline[seconds] = [`Seconds: ${workoutTime - time}`];
                }

                if (workout < workouts) {
                    seconds += 1;
                    workoutTimeline[seconds] = ['Short break'];
                    for (let time = 0; time < pauseTime; time++) {
                        seconds += 1;
                        workoutTimeline[seconds] = [`Seconds: ${pauseTime - time}`];
                    }
                } else if (round < rounds) {
                    seconds += 1;
                    workoutTimeline[seconds] = ['Long break'];
                    for (let time = 0; time < restTime; time++) {
                        seconds += 1;
                        workoutTimeline[seconds] = [`Seconds: ${restTime - time}`];
                    }
                }
            }
        }
    }

    seconds += 1;
    workoutTimeline[seconds] = [
        'Workout finished',
        `Total time: ${(seconds / 60) | 0} minutes, ${seconds % 60} seconds`
    ];

    return workoutTimeline;
};

runTimeline(buildWorkoutTimeline());


塔丹



* setTimeout(someFunction, 1000)从被调用的那一刻起不会执行1000毫秒,但不能早于1000毫秒。因此,总锻炼时间可能会稍长一些。 requestAnimationFrame可能会产生更好的结果。测试一下。

关于javascript - 嵌套循环输出的时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57100868/

相关文章:

javascript - For 循环返回奇怪的长度

javascript - AngularJS 按字母数字顺序列出项目

javascript - 嵌套标签系统

JAVA如何求14位数值的总和?

php - foreach 在 while 循环中获取行值...?

java - 'while' 语句无法在不抛出异常的情况下完成 - Android

javascript - 从不同的 javascript 函数调用 javascript addEvent 函数

javascript - 使用 Google 的 Platform.js 插件

Java - 在一行中输出最大为用户输入整数的整数

c - bool 表达式看似合乎逻辑,但不起作用