See previous blog post: Scripting a C# application with JavaScript and Jurassic.
My C# application is pretending to monitor stuff and calls a JavaScript plug in. The plug in
- Initializes a function notifyMe() that will be called back each time an event occur
- Calls the monitorEvent(), method which will block the JavaScript execution, but call the callback notifyMe() when needed.
/* * Main.js, JavaScript plug in to implement a custom code when an event occur */ function main() { var counter = 0; function notifyMe(time) { print("[{0}]Notified {1}".format(++counter, time)); return true; } print("Monitoring, press ESC to stop the monitoring.") MonitorEngine.monitorEvent("stuff", notifyMe); }In the pure tradition of JavaScript there is only one thread executing everything, therefore when the callback function notifyMe() is called we are safe to access everything. Obviously the closure is working and we can use the variable counter, declared in the parent function.
My current implementation of the MonitorEngine object is really simple and does not support nesting asynchronous methods because I use one global variable.
In the future I may use a stack to remember the nested callback function, or pass the JavaScript callback function directly to the C# world rather than utilizing the global function __sys__callback__func if Jurassic allow it.
/* Create a singelton object named MonitorEngine No multi threading support obviouly. It is JavaScript after all. */ // Having one global variable does not support nesting asynchronous method call // TODO: Use a stack. Every asynchronous method would first push on the stack // the current call back function, initialize the new callback and at the restore // the previous call back. var __sys__callback__ = null; function __sys__callback__func() { if(__sys__callback__ !== null) { return __sys__callback__.apply(this, arguments); } else return false; } var MonitorEngine = (function () { var _monitorEngine = {}; _monitorEngine.monitorEvent = function (eventType, callBack) { // Initialize internally the callback function if(!sys.isUndefined(callBack)) __sys__callback__ = callBack; var r = __monitorEvent(eventType); } return _monitorEngine; })();And to finish the C# code
class Program { static Random _rnd = new Random(); private static int GetRandomTime() { return _rnd.Next(1, 999); } static void Main(string[] args) { var engine = new Jurassic.ScriptEngine(); // Export the print method to the JavaScript world engine.SetGlobalFunction("print", new Action<object>((o) => { Console.WriteLine(o == null ? "null" : o.ToString()); })); // Export my own api to the JavaScript world engine.SetGlobalFunction("__monitorEvent", new Func<string, bool>((eventType) => { try { Console.WriteLine("Monitor event:{0}".format(eventType)); while (true) { if (Console.KeyAvailable) if (Console.ReadKey().Key == ConsoleKey.Escape) break; var time = GetRandomTime(); System.Threading.Thread.Sleep(time); // Call back internal generic function var r = engine.CallGlobalFunction<int>("__sys__callback__func", time); } return true; } catch (System.Exception ex) { Console.WriteLine(ex.ToString()); Console.ReadKey(); } return false; })); var jsSource = new StringBuilder(1000); // My personal JavaScript libraries var a = Assembly.GetExecutingAssembly(); jsSource.Append(DS.Resources.GetTextResource("dictionary.js" , a)).AppendLine(); jsSource.Append(DS.Resources.GetTextResource("list.js" , a)).AppendLine(); jsSource.Append(DS.Resources.GetTextResource("stack.js" , a)).AppendLine(); jsSource.Append(DS.Resources.GetTextResource("string.js" , a)).AppendLine(); jsSource.Append(DS.Resources.GetTextResource("stringbuilder.js", a)).AppendLine(); jsSource.Append(DS.Resources.GetTextResource("sys.js" , a)).AppendLine(); // Create a nice JavaScript wrapper for my C# Api exposed jsSource.Append(DynamicSugar.DS.Resources.GetTextResource("MonitorEngine.js", a)).AppendLine(); jsSource.Append(DynamicSugar.DS.Resources.GetTextResource("Main.js", a)).AppendLine(); jsSource.Append("main();"); // Call the main function engine.SetGlobalValue("ExitCode", 0); try { engine.Execute(jsSource.ToString()); } catch(Jurassic.JavaScriptException ex) { Console.WriteLine("Error:{0}, Line:{1}\r\n{2}".format(ex.Message, ex.LineNumber, ex.Source)); } Console.WriteLine(engine.GetGlobalValue<int>("ExitCode")); } }The source code is available at jura2.zip
No comments:
Post a Comment