Skip to content

Instantly share code, notes, and snippets.

@MangelMaxime
Last active May 13, 2024 09:43
Show Gist options
  • Save MangelMaxime/e476b6a02021bea2bf7bb47baa79b621 to your computer and use it in GitHub Desktop.
Save MangelMaxime/e476b6a02021bea2bf7bb47baa79b621 to your computer and use it in GitHub Desktop.
Demonstrate how to use native async/await from JavaScript with haxe.
package async;
#if macro
import haxe.macro.Context;
#end
class Async {
public static macro function async(expr:haxe.macro.Expr) {
expr = Context.storeTypedExpr(Context.typeExpr(expr));
return macro untyped __js__("(async {0})", ${expr})();
}
public static macro function await(expr:haxe.macro.Expr) {
expr = Context.storeTypedExpr(Context.typeExpr(expr));
return switch (haxe.macro.Context.typeof(expr)) {
case TInst(_.toString() => "js.Promise", [t]):
var ct = haxe.macro.TypeTools.toComplexType(t);
return macro(untyped __js__("await {0}", ${expr}) : $ct);
default:
throw 'await() is only available for promises';
}
}
public static macro function asyncPromise(expr:haxe.macro.Expr) {
expr = Context.storeTypedExpr(Context.typeExpr(expr));
return macro untyped __js__("(new Promise(async {0}))", ${expr});
}
}
package async;
@:autoBuild(async.AsyncMacro.macroBuild())
class Asyncify {
}
package async;
import haxe.macro.Expr;
#if macro
import haxe.macro.Context;
#end
class AsyncMacro {
public static function macroBuild() {
var fields = Context.getBuildFields();
for (field in fields) {
function e(expr) return { expr : expr, pos : field.pos };
switch(field.kind) {
case FieldType.FFun(f):
// trace(field.name);
// trace(f.expr);
for (m in field.meta) {
switch(m.name) {
case ":async":
var ex = e(EBlock([
e(EReturn(
e(EUntyped(
e(ECall(
e(ECall(
e(EConst(CIdent("__js__"))),
[
e(EConst(CString("(async {0})"))),
e(EFunction(null,
{ args : []
, expr : f.expr
, params : []
, ret : null
}
))
]
)),
[]
))
))
))
]));
f.expr = ex;
default:
}
}
default:
}
}
return fields;
}
}
import async.Asyncify;
import async.Async;
// Here is the ouput generated by the nest program
// Start the program
// Now #1: 2018-09-05 15:05:03
// Now #2 2018-09-05 15:05:05
// Now #3 2018-09-05 15:05:05
// Now #4 2018-09-05 15:05:07
class Main {
static function main() {
        trace("Start the program");
new Runner().run();
    }
}
// Make your class extends Asyncify to use @:async on a member
class Runner extends Asyncify {
public function new() { }
// By using @:async on a member, it's becoming an async context
@:async
    public function run() {
trace("Now #1: " + Date.now().toString());
var now2 = Async.await(resolveAfter2Seconds());
trace("Now #2 " + now2);
// We can also create a local async context by using Async.async
Async.async(function() {
var now4 = Async.await(resolveAfter2Seconds());
trace("Now #4 " + now4);
});
trace("Now #3 " + Date.now().toString());
    }
static function resolveAfter2Seconds() {
        return new js.Promise(function(resolve, reject) {
            untyped __js__("setTimeout")(function() {
                resolve(Date.now().toString());
            }, 2000);
        });
    }
}
@maxless
Copy link

maxless commented Nov 30, 2020

This gist is the first Google result for "haxe async await" and is still relevant, so minor 2020 fixes:

To remove the legacy warnings for untyped js (now it's supposed to be js.Syntax.code() instead), replace the expression macro block with

                    e(EReturn(
                        e(ECall(
                          e(ECall(
                            macro js.Syntax.code, [
                              e(EConst(CString("(async {0})"))),
                              e(EFunction(null, {
                                args : [],
                                expr : f.expr,
                                params : [],
                                ret : null
                              }))
                            ])),
                          []
                          ))
                      ))

Same thing in other places:

        return macro js.Syntax.code("(async {0})", ${expr})();
        return macro js.Syntax.code("(new Promise(async {0}))", ${expr});
            case TInst(_.toString() => "js.lib.Promise", [t]):
                var ct = haxe.macro.TypeTools.toComplexType(t);
                return macro(js.Syntax.code("await {0}", ${expr}) : $ct);

And, as a minor convenience, you can do this:

import async.Async.await;

To use this:

        var now2 = await(resolveAfter2Seconds());

@MangelMaxime
Copy link
Author

Thank you for all the changes, I will take a look at them later on.

I also discovered that someone made a library for exploiting native Javascript promise with Haxe 4: https://github.com/basro/hx-jsasync

I didn't have time to test it yet.

@maxless
Copy link

maxless commented Dec 1, 2020

Thanks for the heads up, I missed this one in my research.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment