Promise VS setInterval

Сразу скажу, прогаю как любитель, все не срочно и не критично.
Но хотелось бы сделать вот что. Разбираю свои небольшие математические скетчи на JS. Там вся анимация на setInterval/clearInterval.

Пример. Пусть:
isPlay - флаг проигрывания
delay - задержка
calculateNewState() - некие вычисления
showCanvas() - отображение

Вычисления достаточно долгие, отображение быстрое.
delay выбирается, чтобы в любом случае быть больше времени работы этих функций. И такой код:

function step()
{
    timerId = setInterval(function() {
        if (! isPlay) {
            clearInterval(timerId);
            return;
        }
        
        calculateNewState();
        showCanvas();
        
        if (! isPlay) clearInterval(timerId);
    },  delay);
}

Работает, при слишком малом delay запинается, но не критично.
И все же хочется сделать это грамотно и без запинок. Для этого, очевидно, и нужны промисы. В целом в них разобрался, сделал даже такой пример. Но как перейти от setInterval к промисам, никак не пойму ((

UPD
Уточняю формулировку. Выполнение calculateNewState() + showCanvas() должно занимать минимум delay сек. Если оно меньше - значит мы ждем оставшееся до delay время и начинаем новый цикл.
А если оно получается больше, тогда дожидаемся корректного завершения функций, и сразу начинаем новый цикл. Думаю async-await для этого и созданы, но сложно найти примеры такого прямого применения.
Функции простые, вычисление массива и его отображение, внутри них нет ни интерфейса, ни setInterval ни async.


Ответы (2 шт):

Автор решения: ksa

Но как перейти от setInterval к промисам, никак не пойму ((

Из приведенного можно сделать нечто такое...

let isPlay = true
const delay = 1000
step()
  .then(_ => console.log('Все'))
  .catch(console.log)
//
async function step() {
    while (isPlay) {
      await calculateNewState(2000),
      await showCanvas(1000)
      console.log('Шаг')
    }
}
document.querySelector('button').addEventListener('click', _ => isPlay = false)
//
async function calculateNewState(delay) {
  await new Promise(res => setTimeout(_ => {
    console.log('calculateNewState завершен')
    res()
  }, delay));
}
//
async function showCanvas(delay) {
  await new Promise(res => setTimeout(_ => {
    console.log('showCanvas завершен')
    res()
  }, delay));
}
<button>Стоп</button>

→ Ссылка
Автор решения: Denis Ivanov

Ура, получилось, спасибо всем за советы!

async function sleep(delay) {
    return new Promise(resolve => {
        setTimeout(() => {
            calculateNewState();
            showCanvas();
            resolve()
        }, delay);
    })
}

async function step(delay) {
    while (isPlay) {
        await sleep(delay)
    }
}

Отображается гораздо плавнее (к слову сказать, у меня delay около 40-50 мс), жаль не получается GIF вставить.

→ Ссылка