Как обещать эту рекурсивную функцию

Ниже приведена простая рекурсивная функция, которая принимает длину и уменьшает ее с помощью setTimeout. Когда длина равна <= 0, все готово.

Как я могу написать эту функцию (на чистом JavaScript), чтобы я мог использовать ее так:

animate(999).then(...)

const animate = length => {
  console.log(length)
  length -= 10
  if (length <= 0) {
    length = 0
    return
  }
  setTimeout(() => {animate(length)}, 10)
}

animate(999)

Обновление:

Это то, что я пробовал. У меня проблема в том, что кажется, что resolve либо не вызывается, либо вызывается по другому обещанию.

const animate = length => {
  return new Promise((resolve, reject) => {
    console.log(length)
    length -= 10
    if (length <= 0) {
      length = 0
      resolve(true)
      return // without this the function runs forever
    }
    setTimeout(() => {animate(length)}, 10)
  })
}

animate(999).then(result => console.log(result))

** Рабочее обновление (но не понимаю его) **

const animate = length => {
  return new Promise((resolve, reject) => {
    console.log(length)
    length -= 10
    if (length <= 0) {
      length = 0
      return resolve(true)
    }
    setTimeout(() => {resolve(animate(length))}, 10)
  })
}

animate(999).then(result => console.log(result))


person Raphael Rafatpanah    schedule 14.03.2017    source источник


Ответы (4)


setTimeout(() => {animate(length)}, 10) не будет работать, это может снова вызвать функцию animate, но никогда не разрешать обещание, созданное в самом внешнем вызове (это то, что исправляет ваше «рабочее обновление» — оно разрешает внешнее обещание с обещанием из рекурсивного вызова, что вызовет их выполнить с тем же результатом).

Вместо того, чтобы столько возиться с обратными вызовами, обещайте асинхронный примитив, который вы используете, setTimeout:

function wait(t) {
    return new Promise(resolve => {
        setTimeout(resolve, t);
    });
}

а затем используйте это в своей функции анимации, чтобы всегда возвращать обещание:

const animate = length => {
  console.log(length)
  length -= 10
  if (length <= 0) {
    length = 0
    return Promise.resolve()
  }
  return wait(10).then(() => {
    return animate(length)
  })
}
person Bergi    schedule 14.03.2017

Новый Promise и, следовательно, его resolve и reject генерируются каждый раз, потому что вы возвращаете новый Promise при каждом вызове функции setTimeout и разрешаете только последний (тот, что внутри if (length <= 0)).

Если бы вы поместили Promise((resolve, reject) над функцией, она бы работала нормально.

Либо так, либо ты носишь resolve, reject с собой.

person zurfyx    schedule 14.03.2017

Вы можете сделать это просто для удовольствия. Однако вы не должны этого делать. Код невозможно поддерживать.

const animate = (length, resolve) => {
  var promise;
  
  if (!resolve) {
    promise = new Promise((newResolve) => {
      resolve = newResolve;
    });
  }
  
  console.log(length);
  length -= 10;
  
  if (length <= 0) {
    length = 0;
    resolve(true);
    return promise;
  }
  
  setTimeout(() => {animate(length, resolve)}, 10);
  
  return promise;
}

animate(999).then(result => console.log(result))

person Sergiu Paraschiv    schedule 14.03.2017

Это отличная работа для async/await:

let wait = ms => new Promise(resolve => setTimeout(resolve, ms));

const animate = async length => {
  for (; length > 0; length -= 10) {
    console.log(length);
    await wait(10);
  }
  return 0;
}

animate(999).then(() => console.log("Done!"));

person jib    schedule 15.03.2017