带有异步/等待的递归 setTimeout

时间:2021-01-24 19:51:25

标签: javascript node.js async-await settimeout

我正在递归地运行 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,没有超时

6 个答案:

答案 0 :(得分:17)

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

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

如果您不想立即执行,则去掉 IIFE 并以 setTimeout() 开始。然后,当您从 ticker() 回调调用 setTimeout() 时,请确保将 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);

答案 1 :(得分:4)

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

根据您的代码

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

答案 2 :(得分:3)

ticker 期望 minutes 作为参数,因此在 setTimeout 中调用它时必须传递分钟。除此之外,setTimeout 期望第一个参数中有一个函数,所以我建议简单地传递一个调用您的 ticker 函数的箭头函数。请检查以下代码:

;(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)

答案 3 :(得分:1)

这里已经有一些很好的解释,但我将稍微重构代码以利用 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 的回调)。

答案 4 :(得分:0)

我认为 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)

答案 5 :(得分:0)

这是一个过于复杂且可能不合适的解决方案,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);