The coroutine does more than act as a parallel thread. In this case, we don’t have to worry because pcall is in play. Usually, you can use a coroutine to capture errors at runtime. In fact, coroutine.resume returns the error message of a broken code segment. So coroutines have two uses - not just acting as an asynchronous function scheduler.
It’s highly unlikely you will hit the 30 second mark. However, if that’s the case, we can run a “promise” system and wait until all saving has happened but initiate them as quickly as possible. This is common in languages like JavaScript or C#.
This will initiate all saving at around the same time and wait to close the server until either they are all saved or it has to be force closed after 30 seconds (Roblox limit).
Note: This only works because coroutines aren’t actual new CPU threads and are “Lua threads” that run asynchronously and not in parallel. Otherwise our “promisesReturned” variable would be forked and the memory address would be different and this wouldn’t work.