Recursive setTimeout with async/await



我正在运行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
  • 第二次呼叫需要1分钟(预期行为)
  • 在第二次调用之后,它开始顺序调用API,没有超时

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

也许你只需要使用一些时间表管理器,如bree?

你的代码

  1. 这里不需要生命。只要调用setTimeout(() => ticker(1), minutes * 60 * 1000)
  2. 同样改变内部setTimeout调用以传递minutes参数,因为现在您只传递undefined。这意味着immediatelysetTimeout

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)

这里已经有了一些很好的解释,但是我打算稍微重构一下代码,以便在不需要外部库的情况下利用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. 服务器启动后等待1分钟
  2. 请求之间总是等待一分钟

它也更容易理解和阅读,因为你使用async/await,它坚持一个单一的范式(而不是使用async/await和setTimeout的回调)。

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

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

最新更新