sebastienros / jint

Javascript Interpreter for .NET
BSD 2-Clause "Simplified" License
4.06k stars 558 forks source link

Promise not resolving, if resolve() invoked from C# callback function #1576

Open Dubiy opened 1 year ago

Dubiy commented 1 year ago

Version used

v3.0.0-beta-2049

Describe the bug

Promise not resolving, if resolve() invoked from C# callback function.

To Reproduce

GIST: https://gist.github.com/Dubiy/a761662f95341bc530574dec9f29e02a

Or code:

using Jint;
using UnityEngine;
using System;
using System.Threading.Tasks;

public class RunJs : MonoBehaviour
{
    private static Engine engine;

    void Start()
    {
        engine = new Engine();

        engine.SetValue("console",typeof(Debug));
        engine.SetValue("setTimeout", new Action<Action<int>, int>(setTimeout));

        engine.Execute(@"
            function delayResolve(delay) {
                return new Promise(function(resolve) {
                    setTimeout(function() {
                        console.log('onTimeout');
                        resolve('success');
                    }, delay);

                    // resolve('works, if not called from C# inoked function')
                });
            }

            delayResolve(1000)
                .then(res => {
                    console.log('resolved: ' + res);
                })
                .catch(res => {
                    console.log('rejected: ' + res);
                })
            ;
        ");

        void setTimeout(Action<int> callback, int delay)
        {
            Task.Delay(delay).ContinueWith(_ => callback(delay));
        }
    }
}

Expected behavior

onTimeout
resolved: success

Actual behavior

onTimeout
lahma commented 1 year ago

Jint doesn't support multi-threading and that's what your Task.Delay is doing.

Dubiy commented 1 year ago

Is there some alternative solution how to resume js code execution after c#? As far I understrand it's quite popular problem (fetch, setTimeout, etc...)

lahma commented 1 year ago

I think this would need some investigation and hopefully a PR that would both include tests and a remedy 😉

Looooong commented 1 year ago

For me, Promise doesn't even resolve on JS side:

new Promise(function (resolve) {
  log('Promise!')
  resolve(null)
}).then(function () {
  log('Resolved!')
})

Expected output:

Promise!
Resolved!

Actual output:

Promise!
lofcz commented 7 months ago

Can confirm the behavior is still the same as of 3.0

tom-b-iodigital commented 7 months ago

You can use c# tasks, they are converted to promises and awaited correctly.

so looking at the first example here, this would work:


using Jint;
using System;
using System.Threading.Tasks;

var engine = new Engine();

engine.SetValue("log", new Action<object>(Console.WriteLine));
engine.SetValue("setTimeout", new Func<int, Task>(setTimeout));

engine.Evaluate(@"

            setTimeout(10000)
                .then(res => {
                    log('resolved');
                })
                .catch(res => {
                    log('rejected');
                })
            ;
        ");

async Task setTimeout(int delay)
{
    await Task.Delay(delay).ConfigureAwait(false);
}

Just your standard C# and JS async/await way of working :)

I prefer to write it like this:


var engine = new Engine();

engine.SetValue("log", new Action<object>(Console.WriteLine));
engine.SetValue("setTimeout", new Func<int, Task>(setTimeout));

engine.Evaluate(@"
        (async () => {
            await setTimeout(1000);
            log('resolved');
        })();
        ");

async Task setTimeout(int delay)
{
    await Task.Delay(delay).ConfigureAwait(false);
}