That seems rather convoluted. The generator function is gratuitous, and the promise stuff is unfortunately verbose (pity there’s no async version of setTimeout built in). I’d write it with a recursive function, this way:
function t(){let m=Date.now(),s=Math.ceil((m+10)/1000);setTimeout(()=>{console.log(s);t()},s*1000-m)}t()
That maintains the accuracy, but frankly just a 1000ms interval will probably be good enough in most cases, though it’ll drift all over the second (e.g. it loses about a millisecond each tick on my machine under Node), and it’s certainly much simpler to reason about.
I mixed up two things I was trying to do. I was writing an async generator and making it so inside the loop it could decide what to do with the delay. That way you could add a half tick if you wanted. :) But here's how I would rewrite it:
async function* tickSeconds() {
while (true) {
const m = new Date().valueOf()
const s = Math.ceil((m + 10) / 1000)
await new Promise((resolve, _) => {
setTimeout(() => resolve(), s * 1000 - m)
})
yield s
}
}
for await (const s of tickSeconds()) {
console.log(s)
}
I prefer to inline some obvious stuff that is missing than do a paradigm shift to using callbacks or build up an ad hoc library of misc functions.
Still not convinced the generatorness is worthwhile; I’d split out the Promise/setTimeout dance into a `sleep(ms)` or `sleepUntil(date)` function before replacing a simple while loop that logs with a infinite generator and for loop that logs. But certainly if you were scaling it up further you’d want to head in this general direction.
for (const [s, d] of (function*() { while (true) { const m=new Date().valueOf(), s=Math.ceil((m+10)/1000); yield [s, s*1000-m] } })()) { await new Promise(r => { setTimeout(()=>r(), d) }); console.log(s) }