Passing js callback function as parameter to Java addJavascriptInterface - javascript

Is it possible for arbitrary functional objects in js to be passed to Java side, so java can call that js object? (a callback)
for example, (access is a function that already registered to call a Java function)
access(function(blabla){
...
});
Are there any great techniques that can deal with anonymous callback function in java?

You just need some sort of key to reference your individual callback anonymous function. I think this is how jsonp is used sometimes too.
Javascript:
var callbacks = [];
function registerCallback(callback)
{
callbacks.push(callback);
return callbacks.length - 1;
}
function getCallback(index)
{
var callback = callbacks[index];
callbacks[index] = null;
TryGCCallbacks();
return callbacks;
}
//The array index positions must be preserved, but when it's empty, we should restart the array so it doesn't grow forever
function TryGCCallbacks()
{
var isCallbackRemaining = false;
for (var i = 0; i < callbacks.length; i++)
{
if (callbacks[i] == null) {
continue;
}
isCallbackRemaining = true;
break;
}
if (isCallbackRemaining == false)
{
callbacks = []; //reset
}
}
Java:
#org.xwalk.core.JavascriptInterface
public String TestCallback(String someData, String onSuccess, String onError){
xwalk.evaluateJavascript("getCallback(" + onSuccess + ")('success');", null);
}
Using this from Javascript:
var onSuccess = registerCallback(function(r) {
console.log(r);
});
TestCallback('lalala', onSuccess, null);

Related

Collect values as I recurse tree in javascript

I understand basic recursion, but this problem has be stumped. I have a tree structure set up in a database, where each node(row) has an id and parent id.
I need a function that can run and in the callback return an array of all the descendants of a particular node given its id.
I've been able to put together a function that can print out all of the values, but I can't figure out how to capture them and return them in the callback. I know the base case isn't set up correctly, as I'm not sure what it should even be.
I'd appreciate any help! Thank you!
// My "database"
var nodes_collection = [
{id:"id1",name:"name1",parentid:"."},
{id:"id2",name:"name2",parentid:"id1"},
{id:"id3",name:"name3",parentid:"id1"},
{id:"id4",name:"name4",parentid:"id2"},
{id:"id5",name:"name5",parentid:"id3"},
{id:"id6",name:"name6",parentid:"id3"},
{id:"id7",name:"name7",parentid:"id5"},
{id:"id8",name:"name8",parentid:"id7"},
{id:"id9",name:"name9",parentid:"id7"},
{id:"id10",name:"name10",parentid:"id9"},
];
// This is NOT a real function, it simply performs the function that the real getChildren does when connected to my database!!!
function getChildren(parentid, callback){
var children = [];
for(var i=0; i < nodes_collection.length; i++){
if(nodes_collection[i].parentid == parentid){
children.push(nodes_collection[i].id);
}
}
callback(children);
}
function allDescendants(parentid, callback) {
getChildren(parentid, function(childNodes) {
if (false) { // Only false because I don't know what my base case should be.
//console.log("done");
} else {
for (var i = 0; i < childNodes.length; i++) {
var child = childNodes[i];
allDescendants(child);
console.log(child); // Here it prints out all the values. How can I capture them? and return them with my callback?
}
}
});
}
allDescendants("id3", function(result){
console.log(result);
});
EDIT:
Due to some confusion, I've changed the code to a bare bones example of what I'm trying to do that can be run locally !!! getChildren() is NOT a real function, it simply performs the function that the real getChildren does when connected to my database!!!
Bottom line:
The code in question works to recursively touch all values. Now how can I store all the values that are currently being outputted via console.log()?
Here's one simple way. We create a result object and an intermediary recursive function, keeping allDescendants as a wrapper. When the recursion is complete, we return the result that now has all the descendants.
JsvaScript code:
// My "database"
var nodes_collection = [
{id:"id1",name:"name1",parentid:"."},
{id:"id2",name:"name2",parentid:"id1"},
{id:"id3",name:"name3",parentid:"id1"},
{id:"id4",name:"name4",parentid:"id2"},
{id:"id5",name:"name5",parentid:"id3"},
{id:"id6",name:"name6",parentid:"id3"},
{id:"id7",name:"name7",parentid:"id5"},
{id:"id8",name:"name8",parentid:"id7"},
{id:"id9",name:"name9",parentid:"id7"},
{id:"id10",name:"name10",parentid:"id9"},
];
// This is NOT a real function, it simply performs the function that the real getChildren does when connected to my database!!!
function getChildren(parentid, callback){
var children = [];
for(var i=0; i < nodes_collection.length; i++){
if(nodes_collection[i].parentid == parentid){
children.push(nodes_collection[i].id);
}
}
callback(children);
}
function allDescendants(parentid, callback) {
let result = [];
let go = function(children){
for (child of children){
result.push(child);
getChildren(child, go)
}
}
getChildren(parentid, go);
callback(result);
}
allDescendants("id3", function(result){
console.log('result: ' + JSON.stringify(result));
});
I propose this:
let obj = [
{id:"id1",name:"name1",parentid:"."},
{id:"id2",name:"name2",parentid:"id1"},
{id:"id3",name:"name3",parentid:"id1"},
{id:"id4",name:"name4",parentid:"id2"},
{id:"id5",name:"name5",parentid:"id3"},
]
function getChilds(obj, parent_id, callback) {
if(obj.length === 0) return;
else if (typeof callback !== 'function'){
throw new Error("The callback must be a function ");
return;
}
let childs = obj.filter(function (c) {return c.parentid == parent_id })
if(childs.length > 0 ){
childs = childs.map(function (c) {return c.id})
}
callback(childs)
}
// Test
getChilds(obj, "id1", function (childs) {console.log(childs)})

Call a returned function from outside its function

I'm trying to call a function that's returned from a function. Here's what I mean:
myFunction.something; // (Wrong)
function myFunction() {
return {
something: function() {
...
}
};
}
When I try calling myFunction.something nothing happens. How can I call a returned function outside of its function?
JSFiddle
var index = 0;
var animID = requestAnimationFrame(myFunction.something);
function myFunction() {
return {
something: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(myFunction.something);
}
};
}
I would first of all recommend using descriptive variable names; utils rather than myFunction, and incrementFrame rather than something, for example. I would second of all recommend reconsidering your approach to code organization and simply putting all of your helper functions directly in an object, then referencing that object:
var index = 0;
var animID = requestAnimationFrame(utils.incrementFrame);
var utils = {
incrementFrame: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(utils.incrementFrame);
}
}
There are a few differences between these approaches, some of them frustratingly subtle. The primary reason I recommend using an object for organization rather than a function which returns an object is because you don't need to use a function for organization; you are unnecessarily complicating your code.
myfunction is not the object that you get from calling myfunction(), it's the function itself and does not have a .something method.
You could call it again (as in myfunction().something()), but a better approach would be to store a reference to the object you've already created:
function myFunction() {
var index = 0;
var o = {
something: function() {
index++;
console.log(index);
if (index < 5) requestAnimationFrame(o.something);
// btw you don't need to cancel anything once you reach 5, it's enough to continue not
}
};
return o;
}
myFunction().something();
Alternatively you might want to drop the function altogether, or use the module pattern (with an IIFE), as you seem to use it like a singleton anyway.
Try this:
myFunction().something()
myFunction() calls the myFunction function
them we use the dot notation on the returned value (which is an object) to find the something member of it
that member is a function too, so add another set of brackets () to call it
Call function after writing it
var index = 0;
function myFunction() {
return {
something: function() {
index++;
console.log(index);
if (index === 5) cancelAnimationFrame(animID);
else animID = requestAnimationFrame(myFunction().something);
}
};
}
var animID = requestAnimationFrame(myFunction().something);

JavaScript (NodeJS) equivalent for PHP's call_user_func_array()

Is there any equivalent function in JavaScript (NodeJS) similar to PHP's
call_user_func_array (http://php.net/manual/en/function.call-user-func-array.php). Which allows to call a function with array of parameters.
In my case, I have to call util.format with parameters that will be in an array.
Here is very simple example trying to show it.
var util = require("util");
function my_fundtion(items) {
var format_string = "";
for (var i=0; i < items.length; i++) {
format_string += " %s";
}
return util.format(format_string, /* here I want to pass items in items array */);
}
PHP has:
call_user_func_array('myfunc', array(1, 2, "foo", false));
While JS has:
myfunc.apply(null, [1, 2, "foo", false]);
The first null goes in the position of an object. You will make use of that if the function is intended to be a method. An example on using such invocation would be to slice array-like objects which are not arrays at all but seem like one (like the arguments object inside of a function):
Array.prototype.slice.apply(arguments, []);
Generally speaking, the syntax is:
(afunction).apply(obj|null, array of arguments)
You can try this:
function my_fundtion(items) {
var format_string = "";
for (var i=0; i < items.length; i++) {
format_string += " %s";
}
var new_items = items.slice();
new_items.unshift(format_string);
return util.format.apply(null, new_items);
}
IMHO that's the wrong approach for a function. Have you tried the following?
function my_fundtion(items) {
return items.join(" ");
}
Because call_user_func_array call function referenced by the function name which is in string. So, in JS we need to use eval.
Example:
const popup = (msg) => {
alert(msg);
}
const subject = {
popup(msg) {
alert(msg);
}
}
So,
eval("popup").apply(null, ['hi'])
eval("subject.popup").apply(null, ['hi'])
Correct me. If i wrong. ;)

How do I call a function stored in a variable that is of indeterminate depth

I looked at this:
Calling a JavaScript function named in a variable
But it doesn't answer my question.
This normally works:
window['class']['sub_class']['function_name'](data);
But now I'm trying to make a general function that can handle any depth:
function callbackFunction(callback, data){
//callback = a.b.c, or a.b, or a
callback = explode(callback);
//I need to be able to call callbackFunction and somehow callback and form their proper form below
window[callback.a](data);
//or
window[callback.a][callback.b](data);
//or
window[callback.a][callback.b][callback.c](data);
}
I believe the duplicate suggested by Bergi will only solve half of your problem. Since your final value will be a function, and since that function is a member of an object, you'll end up executing it in the wrong context (i.e., with the wrong this value).
I suggest you use something like this:
function getCallback(path) {
var arr = path.split('.');
var k;
var fn = window;
while(k = arr.shift()) {
if(typeof fn[k] === "function") {
fn = fn[k].bind(fn);
} else {
fn = fn[k];
}
}
if(typeof fn === "function") return fn;
return function(){};
}
http://jsfiddle.net/7CEd5/
Compare the value of this in the callback with what you get by using the answers to Convert string in dot notation to get the object reference.
You can chain references to objects/sub-objects/etc for however long you want. If you have a point-delimited string (e.g. "document.blah.blah2.method"), then you need to split it to individual tokens (e.g. ["document", "blah", "blah2", "method"]).
Then it's simply a matter of looping through the chain:
var c = window;
for (var i = 0; i < chain.length - 1; i++) {
c = c[chain[i]];
}
c[chain[chain.length-1]](some_arguments);

javascript jquery function is this somehow wrong?

function rebuildJSONObject(){
$.getJSON('services.json', function(data) {
//stof start
var input = data;
var output = { myservices: [] };
for (var key in input) {
if (input.hasOwnProperty(key)) {
for (var i = 0, hostsinfo = input[key].hostsinfo; i < hostsinfo.length; i++) {
output.myservices.push({
'nametag': key,
'hostidn': hostsinfo[i]['hostidn'],
'details': hostsinfo[i]['details'],
'currstatus': hostsinfo[i]['currstatus'],
'currstatusclass': hostsinfo[i]['currstatusclass']
});
}
}
}
//stof end
return output;
});
}
//setting it for use later in the script
var serviceJSONObject = rebuildJSONObject();
I know the stuff going on in the function is working properly cause if I apply it to a click event it works charming. However I would rather load the JSON object into memory once and work with it client side there after unless saved. My Problem is however anywhere I call "serviceJSONObject" I get an "undefined" error.
So How am I doing this wrong and how would I define a variable like this early in the game so the rest of the script can use said variable.
The issue is that output is returned before the callback function is called. You should be able to save the value to serviceJSONObject by using a closure:
function rebuildJSONObject(serviceJSONObject){
$.getJSON('services.json', function(data) {
//stof start
var input = data;
// Use the serviceJSONObject that is passed into rebuildJSONObject
serviceJSONObject = { myservices: [] };
for (var key in input) {
if (input.hasOwnProperty(key)) {
for (var i = 0, hostsinfo = input[key].hostsinfo; i < hostsinfo.length; i++) {
serviceJSONObject.myservices.push({
'nametag': key,
'hostidn': hostsinfo[i]['hostidn'],
'details': hostsinfo[i]['details'],
'currstatus': hostsinfo[i]['currstatus'],
'currstatusclass': hostsinfo[i]['currstatusclass']
});
}
}
}
//stof end
});
}
//setting it for use later in the script
var serviceJSONObject;
rebuildJSONObject(serviceJSONObject);
Why not add a cache property to a function that will store the result of the initial output (loaded via ajax) and returning the saved state to any consecutive call.
function rebuildJSONObject(callback) {
var self = this;
if (typeof self.cache !== 'undefined') {
if (typeof callback === 'function') {
callback(self.cache);
}
return;
}
$.getJSON('services.json', function(data) {
//stof start
var input = data,
output = { myservices: [] };
for (var key in input) {
if (input.hasOwnProperty(key)) {
for (var i = 0, hostsinfo = input[key].hostsinfo; i < hostsinfo.length; i++) {
output.myservices.push({
'nametag': key,
'hostidn': hostsinfo[i]['hostidn'],
'details': hostsinfo[i]['details'],
'currstatus': hostsinfo[i]['currstatus'],
'currstatusclass': hostsinfo[i]['currstatusclass']
});
}
}
}
//stof end
self.cache = output;
if (typeof callback === 'function') {
callback(self.cache);
}
return;
});
}
EDIT: For the first time you will need to call this function asynchronously and supply a callback function, for example
rebuildJSONObject(function(output) {
/*
* Process your output here
*/
console.log(output);
});
Each consecutive time you can again use it synchronously:
console.log(rebuildJSONObject.cache);
There are a couple of problems with this.
The call to getJSON is asynchronous so you need to be careful you don't try to use the results before the call has returned your results.
The way it is at the moment, the results will not be returned to serviceJSONObject. The return output statement is setting the return for the anonymous function, not the return value for rebuildJSONObject, so the results will just disappear. If you want the results to be available elsewhwere in code you will either need to store them in a global variable or access them inside the callback.

Categories

Resources