@JSExport
public static JSPromise<String> getString() {
return JSPromise.create((resolve, reject) -> {
new Thread(() -> {
AppComponent appComponent = DaggerAppComponent.create();
RestClient restClient = appComponent.getRestClient();
resolve.accept("Response is " + restClient.getString("https://example.com/string.json").responseBody());
}).start();
});
}
import {getString} from './teavm/classes.js';
console.log(await getString());
@JSExportthe better.
public static String getString() { AppComponent appComponent = DaggerAppComponent.create(); RestClient restClient = appComponent.getRestClient();return "Response is " + restClient.getString("...").responseBody(); }
I really don't recommend to use async functions, since they unpredictably affect performance and code size. The only use-case for this feature is compatibility: you already have some legacy code that relies on threads and blocking. So I don't want to encourage regular user to use asyncs by introducing features like the one you want.
Anyway, I think it's quite trivial task to write a helper function that wraps lambda into this JSProise/Thread boilerplate.
I really don't recommend to use async functions, since they unpredictably affect performance and code size. The only use-case for this feature is compatibility: you already have some legacy code that relies on threads and blocking. So I don't want to encourage regular user to use asyncs by introducing features like the one you want.
Actually, I'm currently working on a greenfield personal project that I'm using as a vehicle to experiment with TeaVM. So, I'm trying to develop some best practices for apps and components written in TeaVM.
I had always felt that the async feature of TeaVM was a "killer" feature. Is there another answer to `async/await`?
It is a killer feature, but in another sense. Consider you have an application that is already based on Java-style sync approach with threads and synchronization primitives (like Codename One). You want to port this app to JavaScript with as few changes as possible. In this case async is really a killer feature. As well as support for Java and Kotlin in one project.
If you are writing a JavaScript module (not application!) from
the beginning, you better not rely on this feature. It's not only
unpredictable performance issues. It's also unpredictable
behaviour issues. Consider you have a module that exports async
method foo and sync method bar. In this case you can't have a
guarantee that bar is sync. As soon as you have at least *one*
async method, *any* method can become async. In earlier versions
of TeaVM I tried to address this issue by introducing `@Sync`
annotations with compile-time verification. Unfortunately, in
practical cases this produces too much false negatives. In later
versions I deprecated `@Sync` annotation.
E.g. In Javascript if I need to perform a network request to fetch some data, I can do:
const records = await findRecords();
This is really just syntactic sugar around promises. I.e. equivalent to `findRecords().then(records => ...)`
But it is way nicer than using the raw promise syntax.
If I want to "wrap" the findRecords() method in TeaVM, then AFAIK I have two options:
1. Use TeaVM's @Async functionality (or higher-level Java synchronization primitives that depend on it) to provide a "synchronous" API.
2. Use raw promise syntax. E.g. findRecords().then(() -> {...}).
Is there a third option that I'm missing here?
Third option is: use Kotlin. Without direct support in language syntax it's impossible to create a reliable alternative to async/await functionality in JS. Also, Java bytecode has some limitations that make it difficult to properly describe verification errors. For example, Java bytecode only contains line numbers/file names for those line, that are executable, but for abstract methods it's not possible to get source line number.
For CoSpaces we write some low-level performance critical code in Java, and higher-level business logic in Kotlin. We don't rely on TeaVM's async support. Instead, we wrote couple coroutine functions like following:
fun <T> async(block: suspend () -> T): JSPromise<T> = JSPromise { resolve, reject -> block.startCoroutine(Continuation(EmptyCoroutineContext) { result -> result.onSuccess(resolve).onFailure { reject(it) } }) } suspend fun <T> JSPromise<T>.await(): T = suspendCoroutineUninterceptedOrReturn { cont -> then { cont.resumeWith(Result.success(it)) } .catchError { cont.resumeWith(Result.failure(it.asThrowable())) } COROUTINE_SUSPENDED }
Another option is to use raw promise syntax. According to my
experience, *most* async calls just update UI on promise
resolution, and there's no need in something non-trivial.