microsoft / ClearScript

A library for adding scripting to .NET applications. Supports V8 (Windows, Linux, macOS) and JScript/VBScript (Windows).
https://microsoft.github.io/ClearScript/
MIT License
1.77k stars 148 forks source link

Get result of async operation using V8JsEngine #535

Closed enkelmedia closed 1 year ago

enkelmedia commented 1 year ago

Hi!

I'm trying to understand how to get the result from an async javascript operation when using V8JsEngine.

This is similar to what I need to do:

var engine = new V8JsEngine();
var test2 = """
      (async function() {
         html = 'init';
         var req = await fetch('https://www.website.com');
         html = await req.text();
     })()
     """;

engine.Execute(test2);
var html2 = engine.Evaluate<string>("html"); // value always "init"

Basically fetch would return a js-promise and I need to get my hands on that value in the C#-code. I have spent a couple of hours fiddling with this but I can't seem to understand how it would work. Is this supported at all?

ClearScriptLib commented 1 year ago

Hello @enkelmedia,

Thanks for posting your question!

First, please understand that V8JsEngine is not a ClearScript class; you can visit the JavaScriptEngineSwitcher project site for relevant information. Also, please note that fetch is part of the Web APIs rather than a standard JavaScript built-in.

To demonstrate ClearScript's support for async JavaScript, we'll need a minimal fetch implementation:

public sealed class Response {
    private string _html;
    public Task<string> text() => Task.FromResult(_html);
    public static async Task<Response> fetch(string url) {
        using var client = new HttpClient();
        return new Response { _html = await client.GetStringAsync(url) };
    }
}

We can use this class to produce a working version of your sample:

using var engine = new V8ScriptEngine(V8ScriptEngineFlags.EnableTaskPromiseConversion);
engine.AddHostObject("fetch", Response.fetch);
await (Task)engine.Evaluate("""
    (async function() {
        html = 'init';
        var req = await fetch('http://www.example.com');
        html = await req.text();
    })()
""");
Console.WriteLine(engine.Script.html);

Good luck!

enkelmedia commented 1 year ago

Hi!

Thank you so much for the prompt and very informative response!

I was looking closer at the source and realized that V8JsEngine was a part of JavaScriptEngineSwitcher and that I got things wrong here. Thanks for making that clear.

I understand now that there is a difference between the standard JavaScript and the Web APIs that browsers implement on top of JS, I also think that this is why I had issues with my code.

Very helpful pointers here and I'm very thankful for the help!