Friday, April 4, 2014

Jint tutorial 005 - Exporting a C# Asynchronous API to the JavaScript world (Part II)

Reminder : Jint is a Javascript v 5 interpreter for the .NET Framework/Mono/Xamarin allowing to execute scripts in C# applications on Windows, MacOS, iOS, Android and Linux (Anywhere there is a C# 4.x compiler).

This tutorial demonstrates how to write a true asynchronous API and export it to the javaScript world.

This example rely on the extension for Jint named Jint.Ex. Jint.Ex allows asynchronous execution of call back methods with Jint.

I used the class Storage for unit test purpose in the Jint.Ex project.

C#

       
/// <summary>
/// A test class that implement the method read() in synchronous mode and asynchronous mode
/// </summary>
public class Storage
{
    private Func<Jint.Native.JsValue, Jint.Native.JsValue[], Jint.Native.JsValue> _callBackFunction;
    private AsyncronousEngine _asyncronousEngine;

    public Storage(AsyncronousEngine asyncronousEngine)
    {
        this._asyncronousEngine = asyncronousEngine;
    }

    /// <summary>
    /// Execute the reading of the string in a background thread and then request the
    /// execution of the callback function
    /// </summary>
    private void __BackgroundThread(object p)
    {
        var s = read();
        this._asyncronousEngine.RequestCallbackExecution(_callBackFunction, new List<JsValue>() { s });
    }
    /// <summary>
    /// Synchronous api
    /// </summary>
    /// <returns></returns>
    public string read()
    {
        var s = new StringBuilder(1000);
        for (var i = 0; i < 100; i++)
        {
            s.AppendFormat("{0}", "".PadLeft(4096, '*')).AppendLine();
        }
        return s.ToString();
    }
    /// <summary>
    /// Asynchronous api
    /// </summary>
    /// <param name="callBackFunction"></param>
    /// <returns></returns>
    public string read(Func<Jint.Native.JsValue, Jint.Native.JsValue[], 
                       Jint.Native.JsValue> callBackFunction)
    {
        this._callBackFunction = callBackFunction;
        ThreadPool.QueueUserWorkItem(new WaitCallback(__BackgroundThread), null);
        return null;
    }
}
First, the method read(callBackFunction) requests the execution of the method __BackgroundThread() in a background thread and return; Then method __BackgroundThread() execute the task and calls the AsyncronousEngine method RequestCallbackExecution, to request the execution of the call back.
Requesting means that a request to execute the callback function is added at the end of the AsyncronousEngine event queue. When the AsyncronousEngine event loop will process the event, the callback function will be called with the parameter value.

JavaScript

    
var s = null;

storage.read(function(data) {

    s = data;
});

C# Unit tests

 
[TestClass]
public class AsyncApiUnitTests
{
    private AsyncronousEngine _asyncronousEngine;

    private string GetJSVariable(string name)
    {
        var v = Jint.Ex.HelperClass.ConvertJsValueToNetValue(_asyncronousEngine.Engine.Execute(name)
                .GetCompletionValue());
        return v as string;
    }
    private void RunScript(string script)
    {
        _asyncronousEngine = new AsyncronousEngine();
        _asyncronousEngine.EmbedScriptAssemblies.Add(Assembly.GetExecutingAssembly());
        _asyncronousEngine.Engine.SetValue("storage", new Storage(_asyncronousEngine));
        _asyncronousEngine.RequestFileExecution(script, block: true);
    }
    [TestMethod]
    public void Storage_Sync()
    {
        RunScript("storage.sync.js");
        Assert.AreEqual(409800, GetJSVariable("s").Length);
    }
    [TestMethod]
    public void Storage_Async()
    {
        RunScript("storage.async.js");
        Assert.AreEqual(409800, GetJSVariable("s").Length);
    }
}

No comments:

Post a Comment