Nice one! I would mention in the README that you can also statically import JSAsyncTools.await and use it as a “global” function, e.g.
import JSAsyncTools.await;
//...later
var x = await(somePromise);
Maybe also worth mentioning that one can put this into an import.hx file for convenience. I know it’s pretty generic/basic Haxe stuff but a lot of people don’t actually realize you can do this
Same with JSAsync.func: I would actually import it with import JSAsync.func as async, so I can do async(function(...) { ... }) later. Maybe it’s worth considering renaming func to async for that
Great suggestions @nadako.
Since when has it been possible to import static fields? I was not aware of this
Another thing: What are the chances of Haxe using async and await as keywords in the future? Perhaps I should avoid those names to ensure it wont break if/when the keywords are added to haxe?
I think static imports was there since Haxe 3, so, for quite a long time.
Regarding async/await, the current proposed coroutine design doesn’t reserve these keywords, because it proposes lower level coroutines and the idea is that async and await functions are implemented by the user on top of them, but it’s not set in stone still, so yeah maybe it’s a good idea to avoid those for now.
Hello all, thank you Mario for your library.
I have question about one showcase, I would be very very happy if somebody can help me with this, please.
I am trying to create dynamically generating html with facilities. The main problem is in for cycle in initFacilities method where I need to wait while for will be done, then continue. In this example I have two facilities (1 and 2). Thank you so much for any help…
public var facilitiesCollector:Array<Dynamic>;
@:jsasync static function fetchText(url : String, ?init:js.html.RequestInit) {
return js.Browser.window.fetch(url, init).jsawait().text().jsawait();
}
public function new() {
facilitiesCollector = new Array();
var facilitiesIds:Array<Int> = [1,2]; // these numbers are unique ids in database
initFacilities(facilitiesIds); // calling initialisation function
}
private function initFacilities( ?facilityIds:Array<Int> ):Void
{
// TO DO: NEED TO WAIT WHILE FOR WILL BE DONE. HOW??
for(facilityId in facilityIds) {
loadFacilityForm(facilityId, facilitiesCollector.length); // facilitiesCollector.length is used as iterator
}
trace(facilitiesCollector); // THIS TH POINT WHERE I HAVE STILL EMPTY DATA BECAUSE FOR CYCLUS ABOVE IS NOT WAITING WHILE ALL FUNCTIONS WILL BE DONE. CAN YOU HELP ME WITH THIS HOW WRITE IT BY YOUR LIBRARY?
// after catching all data (data, template) from server I can generate HTML form
}
private function loadFacilityForm( facilityId:Int, addressesLength:Int = 0 )
{
fetchText(Configuration.serviceUrl + '/fetch/loadAddressFile',
{
method: 'POST',
mode: js.html.RequestMode.CORS,
headers: {
'Content-Type': 'application/json', // sent request
'Accept': 'application/json' // expected data sent back
},
body: haxe.Json.stringify({
filePath: 'partials/business_address.tpl',
iterator: cast addressesLength,
facilityId: cast facilityId
})
})
.then(response -> {
facilitiesCollector.push(response); // store all data in array
})
.catchError(error -> trace(error));
}
I hope you can understand this example, I would be very glad for your any answer.
Here’s how I’d solve it:
(I haven’t compiled this code so perhaps there’s errors in it)
public var facilitiesCollector:Array<Dynamic>;
@:jsasync static function fetchText(url : String, ?init:js.html.RequestInit) {
return js.Browser.window.fetch(url, init).jsawait().text().jsawait();
}
public function new() {
facilitiesCollector = new Array();
var facilitiesIds:Array<Int> = [1,2]; // these numbers are unique ids in database
initAddressDynamicElem(facilitiesIds); // calling initialisation function
}
// Turn this function into an async function
@:jsasync private function initAddressDynamicElem( ?facilityIds:Array<Int> ):Void
{
// Make a list with all the loadFacilityForm promises:
var loadList = [for(facilityId in facilityIds) loadFacilityForm(facilityId, facilitiesCollector.length)];
// Wait for all the promises to finish using Promise.all
Promise.all(loadList).jsawait();
trace(facilitiesCollector);
}
// Turn this function into an async function
@:jsasync private function loadFacilityForm( facilityId:Int, addressesLength:Int = 0 )
{
try {
var requestOptions = {
method: 'POST',
mode: js.html.RequestMode.CORS,
headers: {
'Content-Type': 'application/json', // sent request
'Accept': 'application/json' // expected data sent back
},
body: haxe.Json.stringify({
filePath: 'prequalis/partials/business_address.tpl',
iterator: cast addressesLength,
facilityId: cast facilityId
})
};
var response = fetchText(Configuration.serviceUrl + '/ajax/loadAddressFile', requestOptions).jsawait();
facilitiesCollector.push(response);
}catch(error) {
trace(error)
}
}
I was wondering how you managed to add “async” and “await” keywords to the compiled JS and noticed that you used js.Syntax.code function to achieve that.
Do I understand correctly that it is not possible to achieve the same behaviour using @async and @await meta tags?
Because as far as I understand you can’t access (and thus modify) compiled source code in macros, can you?
I would just want that Haxe’s async and await look closer to native JS implementation.
For example:
I think it’s possible to do this, it’s just an extra pass that looks for @await and replaces them with calls to jsawait.
However, I think this will break autocompletion. Since I don’t think the macro that replaces @await would run when the language server is queried for completion lists and other typing info inside the function. (I’m not 100% certain so I might be wrong on this)
If haxe ever gets typed metadata, there’s a chance that expression metadata could behave like a macro function, that would help with the autocompletion problem.
Yeah, I’ve also checked that tink_await package. But it does not seem to add async and await keywords to the compiled source code (assuming that I’ve read and understood the source code correctly ).
tink_await does not use JS promises (it’s a more generic solution, supporting all Haxe targets), but its macro code could eventually be an inspiration for jsasync package.
I just checked tink_await and while autocompletion works better than I expected it would, it’s less than ideal.
For example:
var foo = @await myPromise;
foo.| // Autocompletion seems to work fine here
However when doing the following:
(@await myPromise).| // Haxe thinks the expression is still a promise here, you get the promise members as completion options.
Haxe also doesn’t seem to run the macro when hovering the mouse over a symbol to inspect it’s type. Hovering over the foo variable will show that it’s a Promise instead of just T.
Personally I feel like adding @await metadata support would be a case of form over function, the compilation times get worse and the language server gets confused. I’m quite reluctant to add such a feature.