"Tasks" are a neat feature of Rogue. If you tag method with the [task] attribute it becomes an asynchronous method that can be used like a thread. It appears to be concurrent but it is actually executing as a series of updates on the single-threaded main update loop.
Two additional commands can be used inside task methods: 'yield', which pauses execution of the task until the next update, and 'await', which repeatedly yields until an operand task is finished and then evaluates to the return value of that task method.
Calling a task method gives you a "Task" object in return which you can 'await' or 'start'. start()ing a task is like starting a thread; it just starts going. Awaiting a task (only usable from another task, remember) blocks the original task until the second task is complete.
Here's a contrived example of an app initialization class that fetches a JSON list of image URLs from a server, downloads them one at a time, and then loads them as textures. A pre-existing event-based, task-based "HTTPGet" request class is assumed:
class FetchAssets PROPERTIES asset_list = Asset METHODS method init fetch_assets.start method go_to_next_state # ... method fetch_assets [task] local list = await fetch_image_list if (list?) forEach (info in list) asset_list.add( Asset(info.url,info.filepath) ) endForEach await download_each_asset await load_each_asset endIf println "All resources loaded" go_to_next_state method fetch_image_list->PropertyList local response = HTTPGet( "http://mygame.com/image_list.json" ) if (not response.success) return null return PropertyList( response.data ) method download_each_asset [task] forEach (asset in asset_list) await HTTPGet( asset.url, asset.filepath ) endForEach method load_each_asset [task] forEach (asset in asset_list) asset.texture = Texture( asset.filepath ) yield # only load one per frame endForEach endClass
The technical trick to task methods is that the commands in each task method are turned into an object. Local variables become properties and a big 'which' (switch) essentially keeps track of which command to execute next given whatever yields and awaits have happened.
So the compiler converts this task:
method print_task( first:Integer, last:Integer )->Integer [task] local x = first while (x <= last) println x ++x endWhile return x
into something like this that's automatically hooked into the update loop:
method print_task( first:Integer, last:Integer )->Task Task_print_task(first,last) class Task_print_task PROPERTIES ip = 0 first : Integer last : Integer x : Integer result : Integer METHODS method init( first, last ) method execute which (ip) case 1 x = first ++ip case 2 if (not (x <= last)) ip = 3 else println x ++x endIf case 3 result = x ip = -1 # signal "finished" endClass