async and await are syntactic sugars. The essence of async and await is state machine. The compiler will transform your async/await code into a state machine.
At the same time, in order for async/await to be really practicable in real projects, we need to have lots of Async I/O library functions already in place. For C#, most original synchronized I/O functions has an alternative Async version. The reason we need these Async functions is because in most cases, your own async/await code will boil down to some library Async method.
The Async version library functions in C# is kind of like the AsynchronousChannel concept in Java. For example, we have AsynchronousFileChannel.read which can either return a Future or execute a callback after the read operation is done. But it’s not exactly the same. All C# Async functions return Tasks (similar to Future but more powerful than Future).
So let’s say Java do support async/await, and we write some code like this:
public static async Future<Byte> readFirstByteAsync(String filePath) {
Path path = Paths.get(filePath);
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
await channel.read(buffer, 0, buffer, this);
return buffer.get(0);
}
Then I would imagine the compiler will transform the original async/await code into something like this:
public static Future<Byte> readFirstByteAsync(String filePath) {
CompletableFuture<Byte> result = new CompletableFuture<Byte>();
AsyncHandler ah = new AsyncHandler(result, filePath);
ah.completed(null, null);
return result;
}
And here is the implementation for AsyncHandler:
class AsyncHandler implements CompletionHandler<Integer, ByteBuffer>
{
CompletableFuture<Byte> future;
int state;
String filePath;
public AsyncHandler(CompletableFuture<Byte> future, String filePath)
{
this.future = future;
this.state = 0;
this.filePath = filePath;
}
@Override
public void completed(Integer arg0, ByteBuffer arg1) {
try {
if (state == 0) {
state = 1;
Path path = Paths.get(filePath);
AsynchronousFileChannel channel = AsynchronousFileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(100_000);
channel.read(buffer, 0, buffer, this);
return;
} else {
Byte ret = arg1.get(0);
future.complete(ret);
}
} catch (Exception e) {
future.completeExceptionally(e);
}
}
@Override
public void failed(Throwable arg0, ByteBuffer arg1) {
future.completeExceptionally(arg0);
}
}