dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.26k stars 4.73k forks source link

JSImport returning Promise that signals reject #102339

Closed SerratedSharp closed 5 months ago

SerratedSharp commented 5 months ago

Description

What's expected if a I call a JSImport method which returns a promise, and the promise signals a reject?

This code works fine when it signals resolve, but if it signals reject it hangs on the await'd ConditionalSuccess() call. Neither the "Error occurred" nor the "More?" message prints, and if F10 each line it never returns from the ConditionalSuccess().

Is this not supported, or am I doing something wrong?

I also tried .ContinueWith() and checking for a faulted task, but the ContinueWith is never called. I assume workaround would be to handle reject's JS-side, and throw instead.

Reproduction Steps

Create a wasm-browser project, define the following JS in a ES6 module, load the module with JSHost.ImportAsync, then call the below C# code.

    PromisesShim.ConditionalSuccess = function (shouldSucceed) {        
        return new Promise((resolve, reject) => {            
            setTimeout(() => {
                if (shouldSucceed) {
                    resolve();// success                
                } else {
                    reject();// failure  
                }
            }, 500);
        });
    };
        [JSImport("PromisesShim.ConditionalSuccess", "PromisesShim")]
        public static partial Task ConditionalSuccess(bool shouldSucceed);
        //....

        try
        {
            await PromisesProxy.ConditionalSuccess(shouldSucceed: false);// await an async JS method
            Console.WriteLine($"Waited {sw.Elapsed.TotalSeconds:#.0} seconds for ConditionalSuccess.");
        }
        catch(Exception ex)
        {
            Console.WriteLine($"Error ocurred: {ex.Message}");
        }        
        Console.WriteLine($"More?");

Expected behavior

Maybe return a faulted task or throw an exception? I'm not a expert on Promises so I wouldn't presume to know what should happen. But I feel confident that it definitely shouldn't just hang.

Actual behavior

The call to PromisesProxy.ConditionalSuccess() hangs and never returns if the JS Promise calls reject()

Regression?

No response

Known Workarounds

Pass string into .reject()

Configuration

VS 17.10.0 Preview 3

dotnet --info: .NET SDK: Version: 8.0.300-preview.24203.14 Commit: b7e38f457d Workload version: 8.0.300-manifests.c4df6daf MSBuild version: 17.10.0+4f6b1bb28

Runtime Environment: OS Name: Windows OS Version: 10.0.22631 OS Platform: Windows RID: win-x64 Base Path: C:\Program Files\dotnet\sdk\8.0.300-preview.24203.14\

.NET workloads installed: [aspire] Installation Source: SDK 8.0.300-preview.24203, VS 17.10.34804.81 Manifest Version: 8.0.0-preview.5.24201.12/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.sdk.aspire\8.0.0-preview.5.24201.12\WorkloadManifest.json Install Type: Msi

[wasm-experimental] Installation Source: SDK 8.0.300-preview.24203 Manifest Version: 8.0.4/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.current\8.0.4\WorkloadManifest.json Install Type: Msi

[wasm-tools] Installation Source: SDK 8.0.300-preview.24203 Manifest Version: 8.0.4/8.0.100 Manifest Path: C:\Program Files\dotnet\sdk-manifests\8.0.100\microsoft.net.workload.mono.toolchain.current\8.0.4\WorkloadManifest.json Install Type: Msi

Host: Version: 8.0.4 Architecture: x64 Commit: 2d7eea2529

.NET SDKs installed: 6.0.321 [C:\Program Files\dotnet\sdk] 6.0.400 [C:\Program Files\dotnet\sdk] 6.0.406 [C:\Program Files\dotnet\sdk] 8.0.104 [C:\Program Files\dotnet\sdk] 8.0.204 [C:\Program Files\dotnet\sdk] 8.0.300-preview.24203.14 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.26 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.27 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.29 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 7.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 8.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other information

No response

maraf commented 5 months ago

cc @pavelsavara

pavelsavara commented 5 months ago

This should just work normally. This is how we propagate all exceptions from JS. Are you sure this is single-threaded build ?

SerratedSharp commented 5 months ago

Threading is not enabled.

Thanks for confirming expected behavior. After fiddling for a bit, I was able to narrow this down to whether or not I pass a string to reject(). Removed setTimeout and just called Promise.reject.

Similar discussion over at Typescript seemed to settle on reject() without parameters being considered valid: https://github.com/microsoft/TypeScript/issues/23416#issuecomment-382217917

One other thing I observed is when I created a fresh project to reproduce, execution paused and I got an Exception Unhandled undefined popup in VS. However note when I selected Continue the Program.cs catch block on the C# side never saw this exception and none of the messages afterwards printed, which is consistent with the other occurrence that seemed to indicate it hung: image

Minimal reproduction with a wasm-browser project attached. Note I also upgraded to latest VS preview and ran workload install for both wasm-tools and wam-experimental tonight to get latest versions, rebooted, then created the new project in VS:

RejectionIsNoodles.zip