5
$\begingroup$

When writing a game's event loop, I always rely on a function from an external library, like draw() in ProcessingJS for JavaScript, or .after() from tkinter for Python (or DispatchQueue.main.asyncAfter() from GCD for Swift).

Something like this:

import tkinter as tk
window = tk.Tk()

def eventLoop()
    # Do Stuff Here
    
    window.after(1000//framesPerSecond, eventLoop)

eventLoop()
window.mainloop()

It's called an event loop, but it's actually a function, not a loop. If you tried to implement one as an actual loop, like my very first attempt when I had just started learning programming, it wouldn't work because it would "block" the main thread and not allow any user interaction:

// Faulty Event Loop

while(true) {
    // Do Stuff Here
}

This is the difference between synchronous (the while loop) and asynchronous (all the library functions) execution. I'm aware that a programming language doesn't have to be multithreaded to allow asynchronous execution, and that there are native ways of designating a function asynchronous (like async/await in JS), but haven't been able to find any equivalent for a loop construction.

The closest I found was this question on StackOverflow: https://stackoverflow.com/questions/39110762/while-loops-using-await-async but the answers to the question suggest wrapping the loop inside an asynchronous function because the loop itself at the global level wouldn't be asynchronous.

But why not? Why isn't there some syntax such as

// Hypothetical native JS asynchronous loop

async while(true, 5) {
    // Do Stuff Here every 5 seconds
}

that could be used from the global scope to specify an infinite loop with 'spaced-out' iterations that don't block the main thread? Theoretically, it could work by the same mechanism as async/await but using a loop instead of a function.

Would this be problematic to implement, or are there programming languages I'm not aware of that actually do implement something like this?

$\endgroup$
11
  • 1
    $\begingroup$ Apple's Timer object provides basically this, doesn't it? $\endgroup$
    – Bbrk24
    Commented Jul 5, 2023 at 20:10
  • 1
    $\begingroup$ This is basically just JS's setInterval right? It could also just be implemented in any language with async by awaiting a delay at the end of each loop iteration (in fact, most languages that do have async probably provide non-blocking async versions of sleep) $\endgroup$ Commented Jul 5, 2023 at 20:11
  • 1
    $\begingroup$ @QuackE.Duck I don't think Timer is precise enough for an event loop, but I've used it before to update a value on-screen twice a second. $\endgroup$
    – Bbrk24
    Commented Jul 5, 2023 at 20:17
  • 1
    $\begingroup$ @QuackE.Duck It can be a function, but because Objective-C, it can also take a target-selector pair. The only argument to the function is the timer object itself. $\endgroup$
    – Bbrk24
    Commented Jul 5, 2023 at 20:21
  • 2
    $\begingroup$ Python has an async for loop which consumes an asynchronous iterable. Is this what you're looking for? $\endgroup$
    – kaya3
    Commented Jul 6, 2023 at 13:49

1 Answer 1

5
$\begingroup$

There is a language you are aware of that has these loops. JavaScript has a for await .. of loop construction that expects an async iterator, which uses the same Promise-based async as ordinary await.

For your every-five-seconds example, code might look like this:

for await (let value of every5()) {
  // do stuff here every 5 seconds
}
async function* every5() {
  while (true) {
    await wait(5);
    yield 1
  }
}
function wait(secs) { ... } // typical Promise delay

The every5 function is an async generator; it produces an output with every yield and the loop gets given that value to do something with or ignore. You can also implement these manually with more precise control; most likely, a (standard) library would provide built-in every(secs), when(event), even whileEvery(delay, predicate), etc functions as applicable and useful to the application domain.

The Promise system is built around functions, so there will be function calls happening behind the scenes. You don’t have to write them in the surface syntax. You can have as many of these going at once as you like and they will interleave as the callback times or events occur.


There’s no particular problem with this, and any language with existing async/await of a similar form could graft this in without issue, although they are somewhat more complex. There’s nothing that makes it impossible, just an extra bit of language to design, implement, and learn.

It’s a relatively recent addition to JavaScript, which lives in a world where this sort of event-based asynchronicity is extremely common, so it may not meet the bar for other languages to want to expend work on, though there have been others prior as well. It’s also something you can see idiomatically in some functional languages, where the loop part is the complicated bit.

$\endgroup$
2
  • $\begingroup$ Yes, your for await syntax is something I wasn't aware of and exactly the kind of thing I was looking for! Even this still relies on a separately implemented async function though (every5()); so it looks like the answer to "can you have an async loop instead of function" is both 'yes' and 'no.' I gave you +1 when I saw this posted but was waiting to see if any answers got posted for other programming languages before accepting anything. I think you'll be getting the checkmark too :D $\endgroup$ Commented Jul 5, 2023 at 23:57
  • 1
    $\begingroup$ @QuackE.Duck Yes, although in reality that function probably comes from a library (or builtin) - I implemented it the long way here for demonstration. A language could build in while (true, 5) all the same, it's just probably too specific to make its own loop construct in most cases $\endgroup$
    – Michael Homer
    Commented Jul 6, 2023 at 1:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .