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

How to use Task and Task.Run in VBScript? #549

Closed wangjinbiao1985 closed 11 months ago

wangjinbiao1985 commented 11 months ago

I'm having problems. The C# code is below:

engine.AddHostType("CSTask", typeof(Task)); engine.AddHostType("CSAction", typeof(Action));

The VBScript code is below:

Function LoopOut1() '............ End Function

Function LoopOut2() '............ End Function

Function LoopOut3() '............ End Function

CSTask.Run(host.del(CSAction,LoopOut1)) CSTask.Run(host.del(CSAction,LoopOut2)) CSTask.Run(host.del(CSAction,LoopOut3))

But the final results are not executed as expected, they are executed sequentially. LoopOut1 -> LoopOut2 -> LoopOut3

So, what is the correct way to use Task and Task.Run?

ClearScriptLib commented 11 months ago

Hi @wangjinbiao1985,

The VBScript syntax host.del(CSAction,LoopOut1)) calls LoopOut1 and passes its return value to host.del. In other words, you're calling the LoopOut functions directly and creating three tasks that do nothing.

Instead, you probably want to use GetRef:

CSTask.Run(host.del(CSAction,GetRef("LoopOut1")))
CSTask.Run(host.del(CSAction,GetRef("LoopOut2")))
CSTask.Run(host.del(CSAction,GetRef("LoopOut3")))

Note however that VBScript is a single-threaded script engine; that is, it can't execute code simultaneously on multiple threads. Additionally, it has thread affinity, meaning that it can only execute code on the thread that created it.

Given its single-threaded nature, sequential function execution is the best VBScript can do. ClearScript's VBScriptEngine class uses a dispatcher to comply with the thread affinity requirement.

Here's a sample that demonstrates VBScriptEngine with tasks:

using (var engine = new VBScriptEngine()) {

    engine.AddHostObject("host", new HostFunctions());
    engine.AddHostType("CSTask", typeof(Task));
    engine.AddHostType("CSAction", typeof(Action));

    var count = 3;
    engine.Script.done = new Action(() => {
        if (--count == 0) {
            Dispatcher.ExitAllFrames();
        }
    });

    engine.Execute(@"
        Function LoopOut1()
          '............
          done
        End Function
        Function LoopOut2()
          '............
          done
        End Function
        Function LoopOut3()
          '............
          done
        End Function
        CSTask.Run(host.del(CSAction,GetRef(""LoopOut1"")))
        CSTask.Run(host.del(CSAction,GetRef(""LoopOut2"")))
        CSTask.Run(host.del(CSAction,GetRef(""LoopOut3"")))
    ");

    Dispatcher.Run();
}

Unfortunately, because of VBScript's limitations, the LoopOut functions are still executed sequentially, but the order of their execution is no longer predictable.

You could use multiple VBScriptEngine instances to achieve truly concurrent execution of VBScript code, but you'd have to create each instance on a dedicated thread.

Good luck!

wangjinbiao1985 commented 11 months ago

Thank you very much.

ClearScriptLib commented 11 months ago

Please reopen this issue if you have additional questions about this topic. Thank you!