Currently, JS functions called via RegisteredFunction.Invoke must return a value. As a developer, I would like to have a simple method to call a JS function that does not return something. At least, RegisteredFunction.Invoke should fail gracefully or the fact that the function must return a value should be documented.
Sounds reasonable. We have some higher priority items to deal with right now so moving this to the backlog.
@rstropek should this be a separate static class RegisteredAction with Invoke methods that don鈥檛 return any value, I was thinking more about extension methods on Func<> and Action<>
RegisteredAction and RegisteredFunction like in Action and Func? Would be a good and consistent approach.
If I think about it, there would be an advantage of having a single class like RegisteredFunction. Reason: If I would use a search engine to look for such a feature, I would probably use a search term like call javascript function from C#. I would probably not use the term action. Therefore, I would find RegisteredFunction and I would maybe not look for RegisteredAction any further. However, this is a minor issue if both classes would be probably documented and linked.
Having additional extension methods for Func and Action could make code even nicer.
My vote would be single class. If my goal is to execute a JS function, terms Action vs Func dont really come to mind and I'd be afraid could cause more confusion to those not as familiar with dot net already? I would imagine just adding another override like this:
/// Invokes the JavaScript function registered with the specified identifier.
public static void Invoke(string identifier, params object[] args)
And then just updating the summary of the generic method to something like:
/// Invokes the JavaScript function registered with the specified identifier returning the <T> result
update: I forgot we can't overload this with just the return type so would instead favor another method name
I think we shouldn't split it into action and function, that will just be confusing.
@AWulkan how would you propose we鈥檇 do this give a method and it鈥檚 overloads must return the same type, would you propose that the return value is actually passed into the method as a reference? or generating delegates for the JavaScript methods at build/return?
@grahamehorner I'd really shy away from passing in and manipulating a reference and instead pursue maybe just another method name instead. Invoke and InvokeAction maybe? (thinking calling a function would probably be the most common but maybe not?).
@grahamehorner I was thinking more about this and it hit me that although I was expressing my preference above I really didn't provide any reasoning thus making it nonconstructive. I'm a huge fan of Uncle Bob Martins books, Clean Code being the one that I felt really pushed me as a developer (and most recently Clean Coder which pushed me as a professional..two excellent books if you ever get a chance to read them!). In clean code I always think of his tip regarding avoiding the out parameter or using references as inputs to be modified by the method. I believe the main concern is how it inherently goes 'against the flow' most of the time, so as you're scanning through the code we're usually conditioned to expect a function to accept some inputs and return a result. Reference / out params go against that and require more mental ability to figure out whats going on. Not terrible, but remember I'm the guy who always forgets you can't just change the return type when overloading; I don't have a lot of capacity to spare :P lol
I am really curious of your thoughts regarding passing in the reference though and if there might be more to this given the context that I'm accidentally dismissing? Would there be any memory advantages maybe passing the reference through multiple invoke calls? (I _try_ to not get caught in premature optimization but maybe its worth exploring with this one?)
Also, forgive me if this is not proper etiquette but I'm still trying to get a feel for the open source workflow and where to go for certain support. I was curious if you started digging into this at all or not? I tried putting a branch together to explore some options and I usually try to practice TDD, but struggling to figure out how the runtime is being created (so I can try to create a unit test around the Invoke method). Didn't know if you understand that part and could help explain it to me?
@grahamehorner I don't know how it would be solved. I just know that there are libs that return undefined for various functions by design, and Blazor needs to be able to handle that gracefully imo. I guess you could manually add extra JS code to guard against undefined return values, and turn it into something else, but that could add a lot of extra code across your application. All javascript functions return undefined by default if nothing else is specified.
Maybe some of the issue is the method is trying to do too much? I wonder if it wouldn't be better to split the invoke from deserialization?
public static string Invoke(string identifier, params object[] args)
this only invokes and returns the response (if any).
then we could create an extension to make deserialization easier but you're ultimately passing that responsibility onto the caller.
public static T As<T>(this string input)
I could imagine it then as:
// only invoke without caring or expecting a response
RegisteredFunction.Invoke('name');
// when you need to invoke and care about the result
var result = RegisteredFunction.Invoke('name').As<int>();
I鈥檓 now looking into possibly allowing registration of JavaScript functions creating dynamic delegate Func<> or Actions based on their signature, and accessing the via a dynamic method that match the name of the JavaScript method, I鈥檓 also thinking about possibly have all JavaScript return values wrapped in a generic JavaScriptReturn/JavaScriptNullable/JavaScriptUndefined with some helper methods
Helper methods would include
.As<>()
.ThrowWhen<>()
.DefaultReturnWhen<>()
The JS interop APIs have changed in 0.5.0, so this no longer applies. It is not necessary for JS functions to return a value in 0.5.0.
Most helpful comment
Maybe some of the issue is the method is trying to do too much? I wonder if it wouldn't be better to split the invoke from deserialization?
public static string Invoke(string identifier, params object[] args)this only invokes and returns the response (if any).
then we could create an extension to make deserialization easier but you're ultimately passing that responsibility onto the caller.
public static T As<T>(this string input)I could imagine it then as: