Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for async .NET methods #25967

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mattleibow
Copy link
Member

@mattleibow mattleibow commented Nov 19, 2024

Description of Change

This PR makes the call from JS into .NET be able to support async methods (return Task or Task<T>)

This PR has a fair bit of things moving around because of the async and the fact that I wanted to make the code flow the same on all the platforms. The code was getting a bit nested, so I broke things out into methods.

Issues Fixed

Fixes #25968

This pull request includes several changes to improve the handling of asynchronous operations and simplify the codebase for HybridWebView. The most important changes include the addition of new methods for handling asynchronous tasks, refactoring existing methods to use these new async methods, and updating the test cases accordingly.

Asynchronous Handling Improvements:

Test Case Updates:

Codebase Simplification:

@mattleibow mattleibow requested a review from a team as a code owner November 19, 2024 21:56
@mattleibow mattleibow requested review from rmarinho, jsuarezruiz, Eilon and tj-devel709 and removed request for rmarinho and jsuarezruiz November 19, 2024 21:56
Comment on lines -122 to +137
(var contentBytes, contentType) = InvokeDotNet(invokeQueryString);
var contentBytes = await InvokeDotNetAsync(invokeQueryString);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Windows is super easy, it is already async so I just added the await here.

Comment on lines +259 to +272
if (dotnetReturnValue is Task task) // Task and/or Task<T>
{
await task;

// Task<T>
if (dotnetMethod.ReturnType.IsGenericType)
{
var resultProperty = dotnetMethod.ReturnType.GetProperty(nameof(Task<object>.Result));
return resultProperty?.GetValue(task);
}

// Task
return null;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new logic for detecting types.

Comment on lines -144 to +150
var responseData = GetResponseBytes(url);
var (bytes, contentType, statusCode) = await GetResponseBytesAsync(url);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iOS is also easy with async since the APIs are designed to be called in parts.

var fullUri = new Uri(fullUrl!);
var invokeQueryString = HttpUtility.ParseQueryString(fullUri.Query);
var task = Handler.InvokeDotNetAsync(invokeQueryString);
return new WebResourceResponse("application/json", "UTF-8", 200, "OK", GetHeaders("application/json"), new DotNetInvokeAsyncStream(task));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Android is the difficult one, but it seems that the recommendation is to not actually do work in ShouldInterceptRequest but instead do it as part of the response stream reading. I moved the await code into a new stream type: DotNetInvokeAsyncStream

@@ -147,5 +148,116 @@ internal void Disconnect()
{
_handler.SetTarget(null);
}

private class DotNetInvokeAsyncStream : Stream
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably over engineered this, but I am using pipes to allow myself to read and write. I probably could simplify this and just block, but this appears to work.


var bytesRead = 0;

var readResult = _pipe.Reader.ReadAsync().ConfigureAwait(false).GetAwaiter().GetResult();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How dangerous is this?

StartReading();
}

private async void StartReading()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This async void concerns me if the stream throws. I'm imagining a scenario where the method being invoked throws an exception from a property getter and then this crashes the whole process or something?

@@ -137,82 +138,90 @@ public SchemeHandler(HybridWebViewHandler webViewHandler)

[Export("webView:startURLSchemeTask:")]
[SupportedOSPlatform("ios11.0")]
public void StartUrlSchemeTask(WKWebView webView, IWKUrlSchemeTask urlSchemeTask)
public async void StartUrlSchemeTask(WKWebView webView, IWKUrlSchemeTask urlSchemeTask)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

async void alert

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Calling a method that returns Task from JS will throw
2 participants