How to use the setInterval function to display a factorial calculation with a 500ms interval in between each value appearing on screen?

Viewed 177

I need to implement the setInterval function within my program in order to create an interval of 500ms between each value appearing on screen. The program code i am currently using takes an input value from the user and writes out, as well as calculates the factorized answer.

function factorizeFunction(number, numberArray = []) { //this is the function that does the factorization calculations
  if (number == 0 || number == 1) numberArray.push(number);
  else {
    numberArray.push(number);
    factorizeFunction(number - 1, numberArray);
  }
  return numberArray.join(' * ') + ' = ' + numberArray.reduce((a, b) => a * b, 1);
}
document.getElementById("factorialTest").innerHTML = factorizeFunction(number);

I need to have the answer, for Eg: 6 * 5 * 4 * 3 * 2 * 1 = 720 display on my webpage, each value at a time, with a 500ms delay in between displays. So, 6 (500ms delay) * (500ms delay) 5 (500ms delay)... and so forth. I am unsure on how i would go about doing this?

4 Answers

Basically, everything you want printed on your screen, the operators and numbers, need to appear in a 500ms delay. Which means it's best to first obtain an array of these elements:

function getFactorElements(factor) {
  // get array like [1, 2, 3, 4]
  const factorArray = Array.from({ length: factor }, (_, i) => i + 1);

  // calculate result
  const result = factorArray.reduce((a, b) => a * b, 1);

  // insert math operators
  const parts = factorArray
    .reverse()
    .map((part) => [ part, part > 1 ? '*' : '=' ])
    .reduce((a, b) => a.concat(b));

  parts.push(result);

  return parts;
}

With this being your HTML

<div id="factorialTest"></div>

You can create another function which accepts an element and the factor:

async function showFactorCalculation(element, factor) {
  element.innerHTML = '';

  for (const part of getFactorElements(factor)) {
    element.innerHTML += ' ' + part;
    await new Promise((resolve) => setTimeout(resolve, 500));
  }
}

Which you should call like this:

showFactorCalculation(document.getElementById('factorialTest'), 9);

And I've got a working stack for you here ;):

stack

I'm using setTimeout because using setInterval is mainly discouraged

You could envelope your function with setInterval():

function startInterval(number, numberArray){
   setInterval(() => factorizeFunction(number, numberArray), 500);
}

And call startInterval instead of factorizeFuncion when you want the webpage to start showing the numbers.

It was a good excercise.

const factorizeFunction = async num => {
    if (num < 1) return;
    let resDiv = document.querySelector('#res_div');
    let numArr = [];
    while (num >= 1)
        numArr.push(num--);
    for (let i = 0; i < numArr.length; i++) {
        resDiv.append(numArr[i]);
        await _delay(500);
        if (i < (numArr.length - 1))
            resDiv.append(' * ');
        else if (i == (numArr.length - 1))
            resDiv.append(' = ');
        await _delay(500);
    }
    resDiv.append(numArr.reduce((a, b) => a * b, 1));
};
let _delay = (ms = 200) => new Promise((accept) => setTimeout(accept, ms));
factorizeFunction(6);
<div id="res_div"></div>

Three different approaches.

inSequence constructs a promise chain:

const factorizeFunction = () => '6 * 5 * 4 * 3 * 2 * 1 = 720' // your existing function
const inSequence = async (arr) => arr.reduce((acc, f) => acc.then(f), Promise.resolve())
const delayed = (fn, ms = 500) => (...args) => () => new Promise((res) => setTimeout(() => { res(fn(...args)) }, ms))
const tokenize = (str) => str.match(/[\S]+[\s]?/g)
const print = (str) => document.getElementById("output").innerHTML += str

const printFactorialString = (str) => inSequence(tokenize(str).map((t) => delayed(print)(t)))

printFactorialString(factorizeFunction())
<div id="output"><div>

The following approach uses indirect recursion:

const factorizeFunction = () => '6 * 5 * 4 * 3 * 2 * 1 = 720' // your existing function
const delay = (ms = 500) => new Promise((resolve) => setTimeout(resolve, ms))
const slowly = (action) => {
    return async function go ([i, ...remaining]) {    
        if(!i) return
        action(i)
        await delay()
        go(remaining)
    }
}
const tokenize = (str) => str.match(/[\S]+[\s]?/g)
const print = (str) => document.getElementById("output").innerHTML += str

const printFactorialString = (str) => slowly(print)(tokenize(str))

printFactorialString(factorizeFunction())
<div id="output"></div>

This solution uses an async generator and async...await:

const factorizeFunction = () => '6 * 5 * 4 * 3 * 2 * 1 = 720' // your existing function
const identity = (x) => x
const delayed = (fn, ms = 500) => (...args) => () => new Promise((res) => setTimeout(() => { res(fn(...args)) }, ms))
const asyncIterable  = (arr) => async function* () { for(let i of arr) yield i() }
const tokenize = (str) => str.match(/[\S]+[\s]?/g)
const print = (str) => document.getElementById("output").innerHTML += str

const printFactorialString = async (str) => {
    const generator = asyncIterable(tokenize(str).map((t) => delayed(identity)(t)))()
    for await (let x of generator) { print(x) }
}

printFactorialString(factorizeFunction())
<div id="output"></div>

Related