curiosity-ai / h5

🚀 The next generation C# to JavaScript compiler
https://github.com/curiosity-ai/h5
Apache License 2.0
210 stars 30 forks source link

Conversion from es5.Promise<TResult> to Task<TResult> with ToTask() returns an Array object instead of TResult #88

Closed YImhof closed 10 months ago

YImhof commented 1 year ago

In case of conversion from Promise to Task returning a result, the getAwaitedResult method returns the result encapsulated in an array.

For example, the following code never logs the resulting Response object, as response.status is undefined.

internal class Program
{
    static async Task Main(string[] args)
    {
        RequestInit request = new RequestInit()
        {
            keepalive = true
        };
        Response response = await fetch($"{window.location.href}any/call", request).ToTask();
        if (response.status == 404)
            console.log(response);
    }
}

Part of the transpiled code to JS:

switch ($s) {
    case 0: {
        request = { keepalive: true };
        $t1 = System.Threading.Tasks.Task.fromPromise(fetch(System.String.format("{0}any/call", [window.location.href]), request));
        $s = 1;
        if ($t1.isCompleted()) {
            continue;
        }
        $t1.continue($asyncBody);
        return;
    }
    case 1: {
        $tr1 = $t1.getAwaitedResult();
        response = $tr1;
        if (response.status === 404) {
            console.log(response);
        }
    }
}

Variable $tr1 is an array of length 1 having the Response object at index 0. So perhaps, the line response = $tr1; should be response = $tr1[0]; ?

For a complete information, you have to know that i am trying to create a h5 proxy for an existing TS class that returns promises, doing something similar to the above code, like the following:

[External]
[Name("idProvider")]
internal static class IdProvider
{
    [Template("System.Threading.Tasks.Task.fromPromise({this}.isLoginRedirect())")]
    public static extern Task<bool> IsLoginRedirect();
}

Calling IsLoginRedirect makes a call to the server getting an OIdC configuration to be applied, and so is asynchrone. In both cases, getAwaitedResult returns the unexpected array object.

theolivenbaum commented 10 months ago

Hi @YImhof,

The issue seems to be from the implementation of fromPromises:

promise.then(function () {
    tcs.setResult(handler ? handler.apply(null, arguments) : Array.prototype.slice.call(arguments, 0));
}, function () {
    tcs.setException(errorHandler ? errorHandler.apply(null, arguments) : new H5.PromiseException(Array.prototype.slice.call(arguments, 0)));
}, progressHandler);

As you can see, arguments is the calling arguments from the function on promise.then, and it is just passed as the result to the Task.

That's why the signature of Task.FromPromise is

public static extern Task<object[]> FromPromise(IPromise promise);

I think the right way for you to use the template should be instead to pass the argument number you want from the response in the "handler" argument:

[External]
[Name("idProvider")]
internal static class IdProvider
{
    [Template("System.Threading.Tasks.Task.fromPromise({this}.isLoginRedirect(), 0)")]     // <<<<< Add 0 here
    public static extern Task<bool> IsLoginRedirect();
}
YImhof commented 10 months ago

Hello @theolivenbaum,

Thanks a lot for the feed-back and the solution. I tried it and the problem is solved.

Sorry that I missed the point of the input arguments pass-through.

theolivenbaum commented 10 months ago

No worries, I was also not aware of it 😅