creating a callback function and wiring it up - javascript

This is not a jquery question.
I want to create a callback function...so users would use it like so:
myfunc.init('id',{option1: val, option2: val2}, function() {
//in here users can now call methods of myfunc
});
How do I do this within my code. OS once I know my script is ready I want to be able to somehow call this anonymous function.
Hope this makes sense. I often don't.

You can write i t like this:
var myfunc.init(id, options, callbackFunction){
//do whatever you want with id & options
callbackFunction();
}
This will first run everything you want in your init function then run the function supplied as the callback parameter

Something like this:
var myfunc.init = function(id, options, func) {
// call func somewhere at some point
func();
};
Usually callbacks are used because you want to call it at some point at time which is unknown (asynch)... like for example when an AJAX request is complete:
var myfunc.init = function(id, options, func) {
myAjaxRequest("url.com", function() {
// call the func at this point in time
func();
});
};

Related

Passing anonymous functions through jQuery event call without eventData

Given an <input id="foo"> element, I want to call an existing function on blur and pass an anonymous callback to it.
Case 1, simple function call:
function bar(){
alert("I am");
}
$("#foo").blur(bar); //Works fine
Case 2, passing arguments/function:
$("#foo").bind("blur", {func: function(){console.log("I am apple")}}, bar);
function bar(event){
event.data.func(); //Works fine
};
Problem with case 2 is that I'd like to have bar() as a generic function called by more than blur, in which case I would be passing anonymous function directly, not "baked" into a data object. So ideally like this:
function bar(callback){
//Do stuff
callback();
}
bar(function(){console.log("Hello Earth")}); //Works
$("#foo").blur(bar(function(){console.log("Hello world")})); //Doesn't work
Last line doesn't work as function gets executed directly, but it's an example of what I would like to achieve. I guess I possibly could make some typeof checks in bar() to determine what I am receiving, but I wanted to ask if there's a cleaner way to pass anonymous function on blur, instead of changing bar().
...I wanted to ask if there's a cleaner way to pass anonymous function on blur, instead of changing bar().
Not sure quite what you mean by that, you will have to either change bar or wrap bar.
Changing it:
Make bar a handler generator rather than a direct handler:
function bar(callback) {
return function(e) {
// You could use `e` (the event) here and/or pass it on to the callback
callback();
// You might use the callback's return, e.g.:
// return callback();
// ...if you wanted it to be able to `return false`
};
}
Now, bar returns an event handler that will call the callback (in addition, presumably, to doing something of its own; otherwise, it's a bit pointless and you'd just pass the callback directly).
Usage:
$("#foo").blur(bar(function(){console.log("Hello world")}));
Wrapping it
If you literally mean you don't want to change bar, you'll need to wrap it:
function wrap(f, callback) {
return function() {
// You could call `callback` here instead of later if you like
// Call the original function passing along the `this` and
// arguments we received (`arguments` isn't pseudo-code, it's
// a pseudo-array JavaScript creates for you), get its return value
var retval = f.apply(this, arguments);
// Call the callback
callback();
// Return the original function's return value
return retval;
};
}
If you want to avoid letting errors thrown by callback to make it to the event mechanism, you can wrap it in a try/catch.
Usage:
$("#foo").blur(wrap(bar, function(){console.log("Hello world")}));
function bar(callback){
//Do stuff
callback();
}
bar(function(){console.log("Hello Earth")}); //Works
$("#foo").blur(bar.bind(this,function(){console.log("Hello world")})); //Will work
Wrap it and return it as an anonymous function:
function bar(callback){
return function() {
callback();
}
}
// direct
bar(function(){console.log("Hello Earth")})();
// as callback
$("#foo").blur(bar(function(){console.log("Hello world")}));
function bar(){
alert("I am blur");
}
$("#foo").bind('blur',bar);
Last line doesn't work as function gets executed directly
$("#foo").blur(bar(function(){console.log("Hello world")}));
On the last line you're passing the result of bar to the blur() function, not bar itself, it's the same as:
var result = bar(function(){console.log("Hello world")});
$("#foo").blur(result);
The standard method is to wrap this in an anon function:
$("#foo").blur(function() { bar(function(){console.log("Hello world")}) });
This means there are no changes to your other existing functions, as per the question requirement: "instead of changing bar()"

What to do when passing a parameter into a callback function conflicts with the callback's caller

I am using an ID3 reader script to retrieve data from audio files. The basic usage is:
ID3.loadTags(file,function()
{
var attr = ID3.getAllTags(file).attribute;
});
where the anonymous function is a callback function. This is just to provide context, however, I'm not at all sure the problem I'm having is specific to that particular script.
Typically, inside the callback function, you can extract the info you need and then use the DOM to set the innerHTML attribute of whatever element to equal the info you extracted.
Sometimes you're extracting a bunch of info and connecting all together in a long string though, and I'm trying to compartmentalize it a bit more so that my calling function will be a little cleaner. What I want to do is this:
function callingFunction()
{
var file = "whatever.mp3";
var info = getInfo(file);
}
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
}(info));
return info;
}
An object with an attribute is created because it's one of the only ways to simulate passing by reference in JS - pass the object into the callback function, assign the appropriate data to the attribute in the object, and then at the end of calledFunction, return the object to callingFunction.
It doesn't work though. Now, in that code above, if I said passedVar.data = "teststring" instead of assigning it data from dataobj, that would work, so the passing of the object into the callback function is working correctly. But if the object is assigned data from the data object that the ID3 function returns, it doesn't work. It comes back undefined, and furthermore, Chrome's JS debugger says that the object the ID3 function returns is null. This is further confirmed when I do this:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
alert(ID3.getAllTags(file).(any attribute));
}(info));
return info;
}
and no alert box comes up at all. But if remove the parameter being passed into the callback function in the code above, and leave everything else the same, that alert box comes up like it's supposed to.
So, to sum up, when I pass a parameter into the callback function, for some reason, another function of the object that's calling the callback function ceases to work correctly.
Is it possible that passing a parameter into the callback function is somehow conflicting with, or overriding, whatever the ID3.loadTags function is passing into the callback function, and that's why the getAllTags function is failing? Because for some reason when a parameter is passed into the callback function, the getAllTags function is no longer getting all of the information it needs to run properly? That's the only explanation I can think of.
If that's the case, is there a way to work around it? And if that's not what's going on, what is going on?
I have figured one solution, but I feel like it's hacky. I basically create a third function that gets called from the callback function(which itself receives no parameters), that takes as a parameter the object that the getAllTags method returns, extracts data from that object, and assigns it to global variables that other functions can access. So, this:
var globalVar;
function calledFunction(file)
{
//var info = {data: 0};
ID3.loadTags(file, function()
{
thirdFunction(ID3.getAllTags(file));
});
//return info;
}
function thirdFunction(dataobj)
{
globalVar = dataobj.title+etc;
}
But I don't really like that solution, I feel like it goes against the spirit of compartmentalization that I was after in the first place with this.
I'd appreciate any help.
The reason this doesn't work:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
} (info));
// ^^^^^^ --- calls the function immediately
return info;
}
...is that you're calling your anonymous function and passing the result of that call (undefined) into ID3.loadTags. You're not passing a function into it anymore at all.
But the fundamental problem is that you're trying to use the data object before loadTags calls its callback and puts the data on the object.
I suggest that, since its output depends on an asynchronous operation, rather than relying on function return values, you change calledFunction to take a callback function. Here's what it should look like:
function callingFunction() {
getInfo('whatever.mp3', function(info) { // pass a callback function
// info.data is here now
});
}
function getInfo(file, cb) { // accept a callback function as the 2nd param
ID3.loadTags(file, function() {
var tags = ID3.getAllTags(file);
// once your async operation is done, call cb and pass back the return value
cb({
data: tags.title+etc+tags.album+....(it can get long);
});
});
}
This approach avoids problems you were trying to solve by using an object you could pass by reference, and it ensures you only move on once your asynchronous operation (ID3.getAllTags) is complete.

JQuery, Function orchestration

I am stuck on jQuery 1.4.3 on a current project and need some advice on how best to orchestrate the following..
Let's say I have two functions whom both perform ajax calls, and I only want to call the second one if the first one succeeds. With that said, there are also times in my application where I will call function a without needing to call function b. Therefore it wouldn't make sense to put the call to the second function within the first functions success method.
I'd like to do something like,
function doStuff(){
functionA().success( functionb() ).failure();
}
I typically orchestrate by using .done(); but that was introduced in jQuery 1.5, and again I am stuck on 1.4.3 for now.
Sure it makes sense to call it in the functionA() success handler. Just call it conditionally.
// Set a variable to determine if you will need to call functionB()
var youNeedToCallFunctionB = true;
// And call functionA()
functionA();
// Function definition:
function functionA() {
$.ajax({
url: ...,
success: function() {
if (youNeedToCallFunctionB) {
// Call functionB() in the success handler when needed...
functionB();
}
}
});
}
functionB() {
// Some other AJAX call...
}
Even better, pass a parameter to functionA() which determines whether or not to call functionB()
functionA(youNeedToCallFunctionB) {
// same thing as above, but pass the parameter
}
// Called as
functionA(true);

Setting timer after an asynchronous call returns

I have an asynchronous Ajax function which runs a command string at the server side and returns the result to the client. It calls a callback to process the result.
function ajaxCall(commandStr,callback){
var url=......//make a url with the command string
jquery.get(url,function(result){
//process the result using callback
callback(result);
});
}
The asynchronous call (ajaxCall) may take a while to be finished but I want it to do the same command after an interval (1000ms).
I want to write a function that is like this:
function ajaxCallRepeated(interval,commandStr,callback)
I tried closures like this:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(param1,param2,param3){
return (function(){
ajaxCall(param2,function(out,err){
if(param3)param3(out,err);
var functRef = callLater(param1,param2,param3);
setTimeout(functRef, interval);
});
});
}
//the first call
var functRef = callLater(interval,commandStr,callback);
setTimeout(functRef, interval);
}
Then I call it like this:
ajaxCallRepeated(2000,"ls",function(result){
alert(result);
});
But it only runs the command 2 times.
How can I write a function that will reschedule itself after it is called as a callback of an asynchronous function?
PS. I want to fire another Ajax call after the previous one is finished. Also, it worth to mention that axashCallRepeated() will be called with various parameters, so several Ajax calls are running in parallel, but for each commandStr, there is only one Ajax call going on, and after the Ajax call returns, another one will be fired after X seconds.
I would not use setTimeout to trigger the second Ajax call ! Because you never know how long it will take and if it's finished !
As far as you tagged your question right and you ARE using jquery you should consider something like this:
$.ajax({
type: 'POST',
url: url,
data: data,
success: function(){
// The AJAX is successfully done, now you trigger your custom event:
$(document).trigger('myAjaxHasCompleted');
},
dataType: dataType
});
$(function(){
//somehwere in your document ready block
$(document).on("myAjaxHasCompleted",function(){
$.ajax({
//execute the second one
});
});
});
So this would ensure that the ajax post is DONE and was successful and now you could execute the second one. I know its not the exact answer to your question but you should consider on using something like this ! Would make it safer I guess :-)
The key to solve this problem is to save a reference to the closure itself and use it when scheduling the next call:
function ajaxCallRepeated(interval,commandStr,callback){
//This feature uses closures in Javascript. Please read this to know why and how: http://jibbering.com/faq/notes/closures/#clSto
function callLater(_interval,_commandString,_callback){
var closure=(function(){
ajaxCall(_commandString,function(out,err){
if(_callback)_callback(out,err);
setTimeout(closure,_interval);
});
});
return closure;
}
//now make a closure for every call to this function
var functRef = callLater(interval,commandString,callback);
//the first call
functRef();
}
It becomes easier to reason about if you separate things up a bit.
For example, the repetition logic doesn't have to know about AJAX or callbacks at all:
function mkRepeater(interval, fn, fnScope, fnArgs) {
var running;
function repeat() {
if (!running) return;
fn.apply(fnScope, fnArgs);
setTimeout(repeat, interval);
}
return {
start: function() { running = true; repeat(); },
stop: function() { running = false; }
};
}
You can use it like this:
var r = mkRepeater(2000, ajaxFunction, this, ["getStuff", callbackFn]);
r.start();
...
r.stop();

How to add a callback to a function in javascript

I have two javascript functions
function one () {
do something long... like writing jpgfile on disk
}
function two () {
do something fast... like show the file
}
I call it (in jQuery) like this
one ();
two ();
Because function two needs the link file from function one, i need to be sure the execution is completed... so getting the function two in the callback of function one should be the trick.. but how to do that ?
note : I did put an alert ('aaa') between those two functions to let function one complete, and it worked fine... when the alert is commented (removed) nothing works anymore !
You only need to use a callback if you are doing something asynchronous, otherwise it doesn't matter how long something takes, the next function won't run until the first has finished.
A callback is just passing a function as an argument, and then calling it when done.
function one (callback) {
do something long... like writing jpgfile on disk
callback();
}
function two () {
do something fast... like show the file
}
one(two);
Obviously, if you are doing something asynchronous, then you need something that will tell you when it is finished (such as an event firing).
Simple:
function one (callback) {
do something long... like writing jpgfile on disk
if(callback) callback();
}
function two () {
do something fast... like show the file
}
one(two);
Try this,
$.when($.ajax(fuction1())).then(function () {
fuction2;
});
Here fuction1 is your first function to call, and fuction2 is your second function.
I think it's easy if the browser wait for the process inside "one()" to be done before execute the next line of command. The iceberg hit titanic cause it doesn't wait. Then executing this:
one(two) // while two is the callBack parameter
is nothing different from:
one()
two()
I suggest using a setInterval.
function one(){
//--- Write the file to disk
//.....................
}
function runTwo(){
if (check_the_written_file_existence){
clearInterval(t)
two();
}
}
var t = setInterval("runTwo()",500)
The most important point is that if there's an event fires when the "long process" in function "one()" has done, you just need to bind function two to that event. Unless, you must check the result by someway every span of time until it's really done.

Categories

Resources