**Made changes to example to correct a syntax error (all bold), thanks Luc DUZAN for the help. Changed c.target.result to the actual array used to hold the data 'objCollection' in the every last code example.
Problem: I would like to add an argument to a callback that already has parameters.
For this problem, I have functionality that calls a function, DataLayer.GetData, and executes a callback on completion. I pass this callback, LoadResults, with two existing parameter into the function DataLayer.GetData inside an enclosure. The callback function, LoadResults, correctly gets called from DataLayer.GetData and the parameters originally assigned in the calling functionality are correctly passed into LoadResults.
Is there a generic way or industry standard I can unpack the callback add the c.target.result argument and then call the callbackOnComplete callback so that c.target.result ends up as the dataResults argument in LoadResults?
Calling functionality:
var dbCallback = function () { LoadResults('530', material.Id); };
DataLayer.GetData('530', 'TypeIndex', dbCallback);
Data tier function:
DataLayer.GetData = function (indexKey, indexName, callbackOnComplete) {
var _this = this;
var objCollection = new Array();
try {
var trans = _this.transaction(['objectStoreName'], "readonly");
var store = trans.objectStore('objectStoreName');
var index = store.index(indexName);
var request = index.openCursor(indexKey);
request.onerror = function (e) {
...
}
request.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor) {
objCollection.push(cursor.value);
cursor.continue();
}
}
trans.oncomplete = function (c) {
if (callbackOnComplete) callbackOnComplete();
else ...
}
}
catch (e) {
...
return false;
}
}
Callback function:
LoadResults = function(formType, materialCode, dataResults) {
...
}
What I would like to be able to accomplish within the DataLayer.GetData / trans.oncomplete event is add the argument objCollection to the callbackOnComplete callback list of arguments. Something like:
callbackOnComplete.arguments.push(objCollection);
Or is there another means of passing in a callback and its parameters?
Solution identified by Luc DUZAN:
Calling functionality:
DataLayer.GetData.bind(null, '530', material.Id));
Data tier function:
DataLayer.GetData = function (indexKey, indexName, callbackOnComplete) {
var _this = this;
var objCollection = new Array();
try {
var trans = _this.transaction(['objectStoreName'], "readonly");
var store = trans.objectStore('objectStoreName');
var index = store.index(indexName);
var request = index.openCursor(indexKey);
request.onerror = function (e) {
...
}
request.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor) {
objCollection.push(cursor.value);
cursor.continue();
}
}
trans.oncomplete = function (c) {
if (callbackOnComplete) callbackOnComplete.apply(null, [objCollection]);
else ...
}
}
catch (e) {
...
return false;
}
}
Callback function:
LoadResults = function(formType, materialCode, dataResults) {
...
}
In a first time, you should provide to GetData a callback that only require arguments from c.target.result.
It should not be the concern of your GetData to deal with callback that take others parameters.
You can do that easily in ES5 with Function.prototype.bind:
DataLayer.GetData('530', 'TypeIndex', LoadResults.bind(null, '530', material.id));
Then in GetData, you only concern is too call your callback with value from c.target (which if I understood well is an array). For you can use Function.prototype.apply:
callbackOnComplete.apply(null, c.target);
For example if c.target is [1,2,3], this line will be equivalent to:
callbackOnComplete(1,2,3)
Related
Hi I'm trying to understand callbacks in javascript and have come across this code here from a tutorial that I'm following:
var EventEmitter = require('events');
var util = require('util');
function Greetr() {
this.greeting = 'Hello world!';
}
util.inherits(Greetr, EventEmitter);
Greetr.prototype.greet = function(data) {
console.log(this.greeting + ': ' + data);
this.emit('greet', data);
}
var greeter1 = new Greetr();
greeter1.on('greet', function(data) {
console.log('Someone greeted!: ' + data);
});
greeter1.greet('Tony');
Now I notice that the greeter1.on function takes a callback with a parameter. However I'm not sure how this is implemented internally. I tried looking through the nodejs event.js file but I'm still confused. I am aware that there are ways around this specific implementation by using an anonymous function wrapping the callback with parameters but I want to understand how to use the same format as above.
tldr: How can I create my own function that takes a callback and a parameter in the same fashion as greeter1.on above.
Thank you
Your function needs to define a new property on the current instance with the callback passed as an argument, so it can be called later, like so:
function YourClass () {
this.on = function(key, callback) {
this[key] = callback;
}
}
// Usage
const instance = new YourClass();
instance.on('eventName', function (arg1, arg2) {
console.log(arg1, arg2);
});
instance.eventName("First argument", "and Second argument")
// logs => First argument and Second argument
Callback is just passing a function as a parameter to another function and that being triggered. You can implement callback fashion as below
function test(message, callback) {
console.log(message);
callback();
}
//Pass function as parameter to another function which will trigger it at the end
test("Hello world", function () {
console.log("Sucessfully triggered callback")
})
class MyOwnEventHandler {
constructor() {
this.events = {};
}
emit(evt, ...params) {
if (!this.events[evt]) {
return;
}
for (let i = 0, l = this.events[evt].length; i < l; i++) {
if (!params) {
this.events[evt][i]();
continue;
}
this.events[evt][i](...params);
}
}
on(evt, eventFunc) {
if (!this.events[evt]) {
this.events[evt] = [];
}
this.events[evt].push(eventFunc);
}
}
var myHandler = new MyOwnEventHandler();
myHandler.on('test', function (...params) {
console.log(...params);
});
myHandler.emit('test', 'Hello', 'World');
I have a section in my code that looks like this
var locationDefer = $.Deferred();
if (saSel.Company === -1) {
database.getAllLocations().then(function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
});
} else {
database.getLocationsForCompany(saSel.Company).then(function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
});
}
However, since it is basically the same thing twice, just with a different ajax call - is there any way to either have the anonymous function part
function (result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
})
declared as a real function and then just called in the .then() clause, or can I somehow provide the to-be-called-function of the database object?
For the latter, I had something in my mind that could look like this, but I have no clue how to do the last line.
if(saSel.Company === -1) {
fun = 'getAllLocations';
arg = null;
} else {
fun = 'getLocationsForCompany';
arg = saSel.Company;
}
// database.fun(arg).then(function (result) {...});
You can define a function and pass its reference as success callback handler
//Define the function handler
function resultHandler(result) {
var locations = JSON.parse(result.d);
locationDefer.resolve(locations);
}
if (saSel.Company === -1) {
fun = 'getAllLocations';
arg = null;
} else {
fun = 'getLocationsForCompany';
arg = saSel.Company;
}
//Invoke the method using Bracket notation
//And, pass the success handler as reference
database[fun](arg).then(resultHandler);
Additionally, as getLocationsForCompany() and getAllLocations() returns a promise, you shouldn't use $.Deferred() directly return Promise
return database[fun](arg);
I got some methods (methA, methB ...) that need to call the same method methMain in Javascript. This method methMain then need to fetch some data and when it is done do a callback to the method that called it (methA or MethB ...).
I can successfully create a pointer/reference to a method by using what is written here: How can I pass a reference to a function, with parameters?
That solution, and all others I have seen, does not seem to work in the current scope.
This code will not work:
function TestStructure() {
this.gotData = false;
//define functions
this.methA = function (parA) { };
this.methB = function (parb) { };
this.createFunctionPointer = function (func) { };
this.createFunctionPointer = function (func /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(this, allArguments);
};
};
this.methA = function (parA) {
alert('gotData: ' + this.gotData + ', parA: ' + parA);
if (this.gotData == false) {
var fp = this.createFunctionPointer(this.methA, parA);
this.methMain(fp);
return;
}
//...do stuff with data
}
this.methB = function (parB) {
alert('gotData: ' + this.gotData + ', parB: ' + parB);
if (this.gotData == false) {
var fp = this.createFunctionPointer(this.methB, parB);
this.methMain(fp);
return;
}
//...do stuff with data
}
this.methMain = function (func) {
//...get some data with ajax
this.gotData = true;
//callback to function passed in as parameter
func();
}
}
var t = new TestStructure();
t.methA('test');
When methMain do a callback to func (methA or methB) the variable this.gotData will not be set.
Is there a solution for this problem or do I need to re-think the design?
I want to do this to get data with ajax without blocking with async: false.
I am not 100% sure but I think you can solve your problem by doing
this.createFunctionPointer = function (func /*, 0..n args */) {
var args = Array.prototype.slice.call(arguments, 1);
var that = this; //<<< here
return function () {
var allArguments = args.concat(Array.prototype.slice.call(arguments));
return func.apply(that, allArguments);
//here ^^^
};
};
This will cause your partially evaluated function to be called with the same this that created the function pointer. If you want a different scope just change whatever you pass to .apply.
I have an Ajax call with a callback. (I am using YUI for this). The basic call looks like this:
function something() {
var callback1_success = function (o) {…
};
var callback1_failure = function (o) {…
};
var callback1 = {
success: callback1_success,
failure: callback1_failure
};
var callback2_success = function (o) {…
};
var callback2_failure = function (o) {…
};
var callback2 = {
success: callback2_success,
failure: callback2_failure
};
var ajax_params = buildAjaxParams(….);
Y_GET(ajax_params, callback1);
var ajax_params = buildAjaxParams(….); // different stuff
Y_GET(ajax_params, callback2);
} // something
function Y_GET(p_parms, p_callback) {
request = YAHOO.util.Connect.asyncRequest('GET', p_parms, p_callback);
return (request);
} // Y_GET
This all works fine.What I want to do is send the callback to the server, the server will not process the callback parameters and will send them back in the result set.
So, Y_GET will become: Function Y_GET(p_parms, p_callback) {
var parms_to_send = p_parms + “ & passthrough = ” + p_callback;
request = YAHOO.util.Connect.asyncRequest('GET', parms_to_send, local_callback);
return (request);
} // Y_GET
var local_callback = {
success: function (o) {
o.responseText.passthrough.success
},
failure: function (o) {
o.responseText.passthrough.failure
}
}; /* paste in your code and press Beautify button */
if ('this_is' == /an_example/) {
do_something();
} else {
var a = b ? (c % d) : e[f];
}
So, how do I pass a callback function to the server; which returns the name, and call it. If there is another approach, I am open to that, of passing a callback function and acting upon it in the response set.
Thank you.
Off the top of my head, there are a couple of approaches I can think of. eval() is a possibility, but is generally considered something to avoid given the risk of running arbitrary JS code (ultimately this depends on who is providing the string which is being evaled).
I would recommend the following approach:
Create you functions as declarations on a basic JS object.
var Callbacks = {
callback1: function(){ },
callback2: function(){ }
};
Then, use the string returned from your AJAX call as a property indexer into your Callbacks object. I'm not familiar with YUI AJAX requests, but hopefully you get the idea:
var p_callback = function(){
var local_callback = // parse response, get the callback method you want by name/string
Callbacks[local_callback](); // providing arguments as needed, of course
};
YAHOO.util.Connect.asyncRequest('GET', p_parms, p_callback);
By using property accessors on your object you are assured that you are executing only your own callback code, instead of arbitrary JavaScript that may have been included in the response.
eg I have two concurrent AJAX requests, and I need the result from both to compute a third result. I'm using the Prototype library, so it might look something like this:
var r1 = new Ajax.Request(url1, ...);
var r2 = new Ajax.Request(url2, ...);
function on_both_requests_complete(resp1, resp2) {
...
}
One way would be to use polling, but I'm thinking there must be a better way.
Update: An acceptable solution must be free of race conditions.
On the callback function of each request, set a boolean such as
request1Complete and request2Complete
and call on_both_requests_complete(resp1,resp2).
In the handler function, check to see if both booleans are set. If not, just return and fall out of the function. The callback functions should be serialized, in that they cannot happen simultaneously, so this should work. If they could happen in parallel, you would break on a race condition.
This is how I would do it. The approach is a general one, which gives you more flexibility and reuse, and avoids coupling and the use of globals.
var makeEventHandler = function(eventMinimum, callback) {
var data = [];
var eventCount = 0;
var eventIndex = -1;
return function() {
// Create a local copy to avoid issues with closure in the inner-most function
var ei = ++eventIndex;
return function() {
// Convert arguments into an array
data[ei] = Array.prototype.slice.call(arguments);
// If the minimum event count has not be reached, return
if ( ++eventCount < eventMinimum ) {
return;
}
// The minimum event count has been reached, execute the original callback
callback(data);
};
};
};
General usage:
// Make a multiple event handler that will wait for 3 events
var multipleEventHandler = makeMultipleEventHandler(3, function(data) {
// This is the callback that gets called after the third event
console.log(data);
});
multipleEventHandler()(1,2,3);
var t = multipleEventHandler();
setTimeout(function() {t("some string");}, 1000);
multipleEventHandler()({a: 4, b: 5, c: 6});
Output from callback (condensed by Firebug):
[[1, 2, 3], ["some string"], [Object { a=4, more...}]]
Notice that the order of data in the final callback is in order of the calling events, even though the second "event" executes after the third.
To use this in context of your Ajax requests:
var onBothComplete = makeMultipleEventHandler(2, function(data) {
// Do something
...
});
new Ajax.Request(url1, {onComplete: onBothComplete()});
new Ajax.Request(url2, {onComplete: onBothComplete()});
Edit: I've updated the function to force data to always maintain the asynchronously received event data in the synchronously executed order (the previous caveat no longer exists).
Well, you have to remember that the JS implementation in browsers is not really concurrent, and use that to your advantage. So what you would want to do is in each handler check if the other has finished. Example in jQuery:
var other_done = false;
$.get('/one', function() {
if (other_done) both_completed();
other_done = true;
alert('One!');
});
$.get('/two', function() {
if (other_done) both_completed();
other_done = true;
alert('Two!');
});
function both_completed() {
alert('Both!');
}
Based on Justin Johnson's response to this question:
function sync(delays /* Array of Functions */, on_complete /* Function */) {
var complete_count = 0;
var results = new Array(delays.length);
delays.length.times(function (i) {
function on_progress(result) {
results[i] = result;
if (++complete_count == delays.length) {
on_complete(results);
}
}
delays[i](on_progress);
});
}
This assumes each delay accepts one argument: an "on progress" event handler, which takes one argument: the result that the delay is trying to compute. To complete the example in my original question, you'd use it like so:
var delays = [];
delays[0] = function (on_progress) {
new Ajax.Request(url1, {onSuccess: on_progress});
};
delays[1] = function (on_progress) {
new Ajax.Request(url2, {onSuccess: on_progress});
};
function on_complete(results) { alert(results.inspect()); }
sync(delays, on_complete);
The one thing I'm not sure of is whether this avoids a race condition. If the expression ++complete_count == delays.length always happens atomically, then this should work.
You can use the concept where you set temporary variables and wait for the "last" request to go. To do this, you can have the two handle functions set the tmp vars to the return val and then call your "on_both_requests_complete" function.
var processed = false;
var r1 = new Ajax.Request(...);
var r2 = new Ajax.Request(...);
(function() {
var data1;
var data2;
function handle_r1(data) {
data1 = data;
on_both_requests_complete();
};
function handle_r2(data) {
data2 = data;
on_both_requests_complete();
};
function on_both_requests_complete() {
if ( (!data1 || !data2) || processed) {
return;
}
processed = true;
/* do something */
};
}();