Await Expressions
An await
expression can be used to write asynchronous code in a linear fashion. It can be applied to methods that return a Task
or Future, as well as to methods that expect a callback closure as final parameter.
Code can be written as if the methods in question return a value right away, and the compiler will handle the task of unwrapping the provided code and handle the callbacks properly. Under the hood, await
will break the method into different parts, scheduling them to be executed asynchronously once the awaited actions have been completed.
Await with Tasks
Await with Tasks works similar to the "async/await" pattern in other non-Elements languages, such as JavaScript or Visual C#. A method is declared as returning a special Task
type either explicitly, or using language sugar such as the C# async
keyword. The returned Task
instance can be used to keep track of the status, and be notified when the result is available – which the await
keyword abstracts:
method Test: Task<String>;
begin
var task := new Task<String>(-> begin
Thread.Sleep(10000);
exit 'Result!'
end);
task.Start();
exit task;
end;
...
method OtherMethod();
begin
var lMessage := await Test();
writeLn(lMessage);
end;
At the point of the await
in OtherMethod, the actual method containing the await call will finish. All subsequent code (such as the call to writeLn
in the example above) will be wrapped in a helper type, and execute once the task as completed.
Await with Closures
Await can also be used with methods that take a "callback" closure as last parameter. The parameters of the closure will turn into return values of the call. For example consider the following call using Elements RTL's Http
class:
method DownloadData;
begin
Http.ExecuteRequestAsJson(new HttpRequest(URL), (aResponse) -> begin
if assigned(aResponse.Content) then begin
dispatch_async(dispatch_get_main_queue(), () -> begimn
// show data
end);
end;
end);
end;
This code uses two nested closures, first to wait for the response of tne network request, and then to process its results on the main UI thread. With await
this can be unwrapped nicely:
method DownloadData;
begin
var lResponse := await Http.ExecuteRequestAsJson(new HttpRequest(URL));
if assigned(lResponse.Content) then begin
await dispatch_async(dispatch_get_main_queue());
// show data
end;
end;
Note how the parameter of the first closure, aResponse
becomes a local variable, lResponse
, and how await
is used without return value on the dispatch_async
call.
For callbacks that return more than one parameter, await
will return a Tuple, e.g.:
var (aValue, aError) := await TryToGetValueOrReturnError();
...
Await with Asynchronous Sequences
On the .NET platform, await
can also be used to loop an Asynchronous Sequence. Here the await
keyword is followed by the for each
keywords defining a regular for
Loop. Each individual entry in the sequence will be await'ed and the loop will be run for it. Execution after the loop will continue after all items have been processed and the end of the asynchronous sequence has been reached.
var lItems: async sequence of String := ...;
await for each i in lItems do
writeLn(el);
writeLn("Done");
See Also
await
keyword in C#__await
keyword in Swift- Asynchronous Sequences
- Iterators
Await for Closure Callbacks is unique to Elements, and is available in RemObjects C# and Swift, as well.