使用async/await的递归setTimeout

22

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

3
我是唯一一个在考虑 setInterval 的人吗? - Rohit Kashyap
1
@RohitKashyap setInterval 的作用不同。它会在 interval 毫秒后无论如何都调用一个函数。相反,OP 可能希望定时器在请求成功或出错后开始计时。 - Christian Vincenzo Traina
1
每分钟调用一次API。那么为什么不只调用一次API,然后使用setInterval每隔一分钟调用一次API呢?如果您需要更多对setInterval的控制,可以根据您的条件清除间隔。闭包有人吗?此外,OP在finally中使用了setTimeout。这会有什么区别呢?@CristianTraìna - Rohit Kashyap
1
@RohitKashyap 假设 coingecko.global() 函数需要 3 秒才能完成,使用 OP 方法后间隔为 63 秒,而使用 setInterval 则为 60 秒。 - Christian Vincenzo Traina
1
@CristianTraìna,这就是OP想要的,对吧?每分钟调用一次API。我不确定我错过了什么? - Rohit Kashyap
显示剩余2条评论
6个回答

17

它立即调用它,因为这是您的代码所做的。 它立即执行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);

我的天啊,我怎么会这么糊涂?谢谢。 - André Alçada Padez
4
我已经使用JavaScript工作超过十年了,但我不知道setTimeout函数的这种用法,可以直接传递参数! - André Alçada Padez

4

也许你只需要使用一些调度管理器,比如bree

关于你的代码:

  1. 这里不需要使用IIFE。只需调用setTimeout(() => ticker(1), minutes * 60 * 1000)
  2. 同样地,更改内部的setTimeout调用以传递minutes参数,因为现在你只是传递了undefined。这意味着setTimeout会立即执行。

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)

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)。

0

我认为jfriend00的答案已经解决了这个问题。为了可读性,我建议您采用一个不那么功能强大的方法:

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接