Closed rbsdc closed 2 years ago
Automatically adding await
on promises doesn't really emulate the synchronous behavior where there are a lot of edge cases that the developer could observe the behavior difference.
I'm not familiar with FileReaderSync
API but if the Webworkers is not an option for you, then it is not possible to provide a synchronous API on top of I/O operations. Note that nearly all client apps in Google architected by assuming that such APIs are async (e.g. via Promises or ListenableFutures). This is generally good for all platforms since you don't want blocking operations on the UI thread.
Hope it helps.
Thank you very much for your swift reply!
As for the first point - you are of course right. Adding await
to all async method calls when transpiling is far from trivial and might have further unintended implications (in particular, since functions containing await
must be async
themselves, as mentioned in my initial question). Nonetheless, maybe it would be worth evaluating something like @JsAsync(addAwaitToCalls=true)
. Perhaps, this would be possible to implement and useful in some cases, although in some cases it would break the code - however, as described in the above mentioned setting, in some cases the code does not work anyway. (and I would assume, my case is by no means contrived but an actual problem).
As for the second point - I am fully aware of blocking operations, which is why I would wrap the library calls in a Promise
anyway. However, normally, I think, it is best to let the client (i.e. the JavaScript or Java application that uses the library) decide how to handle these problems. This last point only as a general remark.
To summarize: Do I understand it correctly that it is currently not possible to make my case work both in Java and JavaScript (if we leave web workers aside for a moment)? Or are there other possible solutions?
Thank you again!
Note that TeaVM does (roughly) produce async/await in this way, by "recoloring" the functions that are found to call the async function in question. It can't generally work though, there will inevitably be functions that must return synchronously, and where those meet these "re-colored" functions, there must be a compiler error, or wrong code produced. This operation does require whole-world knowledge of the program (so isn't something j2cl could do).
I don't think your idea of addAwaitToCalls=true
actually makes sense, since that means that in turn the function which gets the await
(i.e. the one calling the method that had the annotation) in turn needs its own @JsAsync
to be added to it.
+1 for preferring async styled code generally. Your first bullet point hit on this a bit, but it sounds like you tried to only solve with async/await to emulate blocking instead of staying async through your API - did you consider trying to expose a listener/thenable that would work in the JVM? J2CL's tests include com.google.j2cl.junit.async.AsyncTestRunner
, a simple junit4 Runner that lets a JS-compatible Thenable (see com.google.j2cl.junit.integration.async.data.Thenable
) be adapted into a guava ListenableFuture, so that the wrapping JVM code can actually block on the results. Or, implement a JS and JVM version of Promise, and actually make reading your data asynchronous in both, with the JVM implementation submitting work to an executor as necessary, etc.
Thank you for the reply! I looked at the JS-compatible Thenable
and I made it almost work. Maybe this is a bug? If I use the previous interface method byte[] readRange(int offset, int length);
(mentioned in the initial post) and FileReaderSync
this works fine. However, If use Thenable<byte[]> readRange(int offset, int length);
then it leads to a casting error (Something like Error: Class$obf_1003: Class$obf_1001 cannot be cast to [LClass$obf_1002
). In both cases I correctly retrieve a Uint8array
, which is why I would expect that it works in both cases.
I also tried to wrap the Thenable
in a ListenableFuture
(more specifically SettableFuture
, since listeningDecorator etc. do not seem to work when transpiling with J2CL). This works fine in the JVM, of course, but when I call the get()
in the transpiled JavaScript library this also results in an error, because the future is still pending (something I expected too, but I nevertheless tried). Is there maybe an other way? If I would have to work with Thenable
, I would prefer to somehow block the result, else the code would be sprawled with Thenable
s everywhere. Unfortunately, AsyncTestRunner
was not very enlightening for this case. Perhaps, I also missed something.
I opened a new issue for the possible bug mentioned in the previous comment: https://github.com/google/j2cl/issues/145
Dear J2CL-community,
currently, I am working on a Java library, which I also want to use in JavaScript. The library provides methods to perform operations on certain files. It expects an interface that provides access to the file:
In Java this interface is implemented with
RandomAcessFile
, in JavaScript withFileReader
andBlob::slice
. However, there is a problem on the JavaScript part: FileReader reads asynchronously, which is why my solution, as far as I've tried, doesn't work. So far I had two approaches to solve this problem:@JsType Promise
in Java with native methods and changing the return type ofreadRange
toPromise<Byte[]>
(I also used the experimental (?)@JsAsync
annotation). However, by doing so, this makes the library useless in Java. Please correct me, if I am wrong. Or is there a way I can make this work both in Java and JavaScript?FileReaderSync
. The problem here, however, is that I have to wrap the code in a Web Worker. Since the library will be used in a local JavaScript application, this leads to a whole string of new problems, because Web Workers are blocked in many browsers when running locally. There are some workarounds and solutions to make this also work, but they are not exactly ideal.I would like to keep it as simple as possible. Is there maybe a solution to make it work with the interface mentioned above? Even if
Promises
would work in Java, I would rather do without them, if possible, because it unnecessarily increases the complexity of the code. On the other hand, if I mark a method as@JsAsync
, is it possible that J2CL adds anawait
before method calls (this, of course, entails the function containingawait
is also transpiled asasync
etc.)? Like this, I would not have to deal with native Promises in Java. Thank you very much in advance.