【问题标题】:Recursive setTimeout with async/await带有 async/await 的递归 setTimeout
【发布时间】:2021-01-24 19:51:25
【问题描述】:

我正在递归运行 setTimeout,因此我只想每分钟调用一次 API。

这是我的代码:

;(async function ticker (minutes) {
  try {
    const res = await coingecko.global()
    const { market_cap_percentage } = res.data.data
    dominance = { btc: market_cap_percentage.btc, eth: market_cap_percentage.eth }
    console.log({ dominance })
  } catch (ex) {
    console.log(ex.message)
  } finally {
    setTimeout(ticker, minutes * 60 * 1000)
  }
})(1)

问题是:

  • 当我启动服务器时,它会立即调用 API
  • 拨打第二个电话需要一分钟(预期行为)
  • 第二次调用后,它开始按顺序调用 API,没有超时

【问题讨论】:

  • 只有我在想setInterval吗?
  • @RohitKashyap setInterval 做了不同的工作。无论如何,它都会在interval 毫秒后调用一个函数。相反,OP 可能希望计时器在请求成功或出错后启动
  • 每分钟调用一次 API。那么为什么不调用一次API,使用setInterval每隔一分钟调用一次API。如果您需要进一步控制setInterval,请根据您的条件清除间隔。关闭任何人?而且,OP 在finally 中使用setTimeout。究竟会有什么不同? @CristianTraìna
  • @RohitKashyap 假设承诺 coingecko.global() 需要 3 秒来实现,在 OP 方法中,间隔是 63 秒,而不是 setInterval 它将是 60 秒
  • @CristianTraìna 是的,这就是 OP 想要的吧?每分钟调用一次 API。不确定我在这里缺少什么?

标签: javascript node.js async-await settimeout


【解决方案1】:

它会立即调用它,因为您的代码就是这样做的。它立即执行ticker(1) 函数调用。

当您从setTimeout(ticker, ...) 调用ticker 时,您没有将minutes 参数传递给该函数 - 这就是setTimeout() 没有正确延迟的原因。

如果您不希望它立即执行,那么请去掉 IIFE 并以 setTimeout() 开头。然后,当您从setTimeout() 回调中调用ticker() 时,请务必将minutes 参数传递给它,要么将第三个参数传递给setTimeout(),要么为其创建一个小回调函数。

这是一个实现:

async function ticker(minutes) {
  try {
    const res = await coingecko.global()
    const { market_cap_percentage } = res.data.data
    dominance = { btc: market_cap_percentage.btc, eth: market_cap_percentage.eth }
    console.log({ dominance })
  } catch (ex) {
    console.log(ex.message)
  } finally {
    setTimeout(ticker, minutes * 60 * 1000, minutes);
  }
}

setTimeout(ticker, minutes * 60 * 1000, 1);

【讨论】:

  • 天哪,我怎么这么瞎??谢谢
  • 另外,我已经使用 javascript 十多年了,我不知道 setTimeout 签名,您可以在其中传递这样的参数!
【解决方案2】:

也许您只需要使用一些日程管理器,例如bree

从您的代码开始

  1. 这里不需要IIFE。只需致电setTimeout(() => ticker(1), minutes * 60 * 1000)
  2. 同样更改内部setTimeout 调用以传递minutes 参数,因为现在您只需传递undefined。这意味着immediately 代表setTimeout

【讨论】:

    【解决方案3】:

    tickerexpects minutes作为参数,因此在setTimeout 中调用它时必须通过分钟。除此之外,setTimeout 在第一个参数中需要一个函数,所以我建议简单地传递一个箭头函数来调用你的 tickerfunction。请检查以下代码:

    ;(async function ticker (minutes) {
      try {
        const res = await coingecko.global()
        const { market_cap_percentage } = res.data.data
        dominance = { btc: market_cap_percentage.btc, eth: market_cap_percentage.eth }
        console.log({ dominance })
      } catch (ex) {
        console.log(ex.message)
      } finally {
        setTimeout(() => ticker(minutes), minutes * 60 * 1000)
      }
    })(1)
    

    【讨论】:

      【解决方案4】:

      这里已经有了一些很好的解释,但我将稍微重构代码以利用 async/await 而不需要外部库。

      首先,让setTimeout 成为一个异步函数:

      async function sleep(ms) {
        return new Promise(res => setTimeout(res, ms));
      }
      

      接下来,让我们用它来代替setTimeout

      (async function ticker (minutes) {
        do {
          await sleep(minutes * 60 * 1000);
          await loadData();
        } while (true);
      
        async function loadData() {
          try {
            const res = await coingecko.global()
            const { market_cap_percentage } = res.data.data
            dominance = { btc: market_cap_percentage.btc, eth: market_cap_percentage.eth }
          } catch (err) {
            console.error(ex.message);
          }
        }
      })(1)
      

      除了

      1. 服务器启动后等待一分钟
      2. 请求之间总是等待一分钟

      它也更容易理解和阅读,并且由于您使用 async/await,它坚持单一范例(而不是使用 async/await 和 setTimeout 的回调)。

      【讨论】:

        【解决方案5】:

        我认为jfriend00's answer 已经解决了这个问题。为了便于阅读,我建议您使用功能较少的方法:

        ;(async function ticker (minutes) {
          while(true) {
            await new Promise(res => setTimeout(res, minutes * 60 * 1000));
            try {
              const res = await coingecko.global()
              const { market_cap_percentage } = res.data.data
              dominance = { btc: market_cap_percentage.btc, eth: market_cap_percentage.eth }
              console.log({ dominance })
            } catch (ex) {
              console.log(ex.message)
            }
          }
        })(1)
        

        【讨论】:

          【解决方案6】:

          这是一个过于复杂且可能不适合的解决方案,setInterval。我只是想展示我们如何使用setInterval 来实现相同的行为。

          const fetch = require('isomorphic-fetch')
          const url = 'https://jsonplaceholder.typicode.com/posts';
          
          
          const closeThis = () => {
            let ids = [];
            async function executeThisShiz(minutes) {
              ids.forEach((id) => {
                clearInterval(id);
              })
              try {
                let res = await fetch(url);
                res = await res.json();
                console.log('yay')
                return res;
              } catch(e) {
                  // maybe abort the timer idk?
                  console.log('error', e);
              } finally {
                id = setInterval(() => {
                    executeThisShiz(minutes);
                }, minutes*60*1000);
                ids.push(id);
              }
            }
            return {
              ids,
              executeThisShiz
            }
          }
          
          const {executeThisShiz} = closeThis();
          executeThisShiz(0.1);
          

          【讨论】:

            猜你喜欢
            • 2020-07-26
            • 2012-11-28
            • 2021-08-25
            • 1970-01-01
            • 1970-01-01
            • 2016-01-22
            • 2017-11-08
            相关资源
            最近更新 更多