-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Advanced Async JavaScript Binding (JSB)
Work In progress, more coming soon!
Before starting here please make sure you've read https://github.com/cefsharp/CefSharp/wiki/General-Usage#async-javascript-binding-jsb as it's important you understand the fundamentals of how to bind an object as they are not covered in this article.
- Can Execute at a later point in time
- Can return a result
- Can pass in simply structs/classes as param
- Takes param array so multiple arguments can be passed
A IJavascriptCallback is a proxy for a method in Javascript
. You can call a bound method and pass in a reference to a function, then at some later point in your .Net
code you can execute the IJavascriptCallback.ExecuteAsync method and the Javascript function will be called.
public void TestCallback(IJavascriptCallback javascriptCallback)
{
const int taskDelay = 1500;
Task.Run(async () =>
{
await Task.Delay(taskDelay);
using (javascriptCallback)
{
await javascriptCallback.ExecuteAsync("This callback from C# was delayed " + taskDelay + "ms");
}
});
}
function MyCallback(string msg)
{
alert("My callback returned - " + msg):
}
boundAsync.testCallback(MyCallback);
Starting in M108
it's possible for IJavascriptCallback.ExecuteAsync
to execute a function that returns a Promise
. (Prior versions it was possible with lots of extra code).
let callback = function ()
{
return new Promise((resolve, reject) =>
{
setTimeout(() =>
{
resolve('Hello from Javascript');
}, 300);
});
};
//Call our bound object passing in our callback function
const result = await boundAsync.javascriptCallbackEvalPromise(callback);
In your .Net App
you simply need to call the IJavascriptCallback.ExecuteAsync
.
public async Task<string> JavascriptCallbackEvalPromise(IJavascriptCallback callback)
{
//Do some processing then execute our callback
//The Task will be resolved when the Promise has either been resolved or rejected
var response = await callback.ExecuteAsync(msg);
//For this very simple example the result is 'Hello from Javascript'.
return (string)response.Result;
}
By default passing primitive types is supported when passing parameters to methods. To support passing POCO objects you register your object with BindingOptions.DefaultBinder
passed as the options
param. Your could would look like:
//For .Net 4.x
browser.JavascriptObjectRepository.Register("mathService", new MathService(), isAsync: true, options: BindingOptions.DefaultBinder);
//For .Net Core/Net 5.0 (only async object registration is supported)
browser.JavascriptObjectRepository.Register("mathService", new MathService(), options: BindingOptions.DefaultBinder);
Supported types include:
- Primitives (int, string, bool, etc)
- Arrays of Primitives (int[], string[], bool[], etc)
- Plain old C# Objects (POCO) (simple request response objects, sub classes are supported)
- Structs
- Arrays/Lists/Dictionaries
A simple MathService
class that performs a multiplication in c#, you can of course do much more complex things, make database calls, etc
//A simple service implemented in C#
public class MathService
{
public MultiplyResponse Multiple(MultiplyRequest request)
{
if (request == null)
{
return new MultiplyResponse { Success = false };
}
var result = request.Num1 * request.Num2;
return new MultiplyResponse { Success = true, Result = result };
}
}
public class MultiplyRequest
{
public int Num1 { get; set; }
public int Num2 { get; set; }
}
public class MultiplyResponse
{
public bool Success { get; set; }
public int Result { get; set; }
}
//For legacy reasons objects returned from methods aren't converted to camel case, we use the new CamelCaseJavascriptNameConverter
//If you don't want any name conversion then set browser.JavascriptObjectRepository.NameConverter = null
browser.JavascriptObjectRepository.NameConverter = new CefSharp.JavascriptBinding.CamelCaseJavascriptNameConverter();
//For .Net 4.x
browser.JavascriptObjectRepository.Register("mathService", new MathService(), isAsync: true, options: BindingOptions.DefaultBinder);
//For .Net Core/Net 5.0 (only async object registration is supported)
browser.JavascriptObjectRepository.Register("mathService", new MathService(), options: BindingOptions.DefaultBinder);
(async function()
{
//Bind the mathService object we registered in C#
await CefSharp.BindObjectAsync("mathService");
//We now have a mathService object available in the global scope, can also be accessed via window.mathService
let response = await mathService.multiple(({Num1:12,Num2:12}));
//Without specifying browser.JavascriptObjectRepository.NameConverter = new CefSharp.JavascriptBinding.CamelCaseJavascriptNameConverter(); in C# above
// you would have response.Result as the property name here.
var result = response.result;
})();
TODO: