Create a custom callback in JavaScript - javascript

All I need to do is to execute a callback function when my current function execution ends.
function LoadData()
{
alert('The data has been loaded');
//Call my callback with parameters. For example,
//callback(loadedData , currentObject);
}
A consumer for this function should be like this:
object.LoadData(success);
function success(loadedData , currentObject)
{
//Todo: some action here
}
How do I implement this?

Actually, your code will pretty much work as is, just declare your callback as an argument and you can call it directly using the argument name.
The basics
function doSomething(callback) {
// ...
// Call the callback
callback('stuff', 'goes', 'here');
}
function foo(a, b, c) {
// I'm the callback
alert(a + " " + b + " " + c);
}
doSomething(foo);
That will call doSomething, which will call foo, which will alert "stuff goes here".
Note that it's very important to pass the function reference (foo), rather than calling the function and passing its result (foo()). In your question, you do it properly, but it's just worth pointing out because it's a common error.
More advanced stuff
Sometimes you want to call the callback so it sees a specific value for this. You can easily do that with the JavaScript call function:
function Thing(name) {
this.name = name;
}
Thing.prototype.doSomething = function(callback) {
// Call our callback, but using our own instance as the context
callback.call(this);
}
function foo() {
alert(this.name);
}
var t = new Thing('Joe');
t.doSomething(foo); // Alerts "Joe" via `foo`
You can also pass arguments:
function Thing(name) {
this.name = name;
}
Thing.prototype.doSomething = function(callback, salutation) {
// Call our callback, but using our own instance as the context
callback.call(this, salutation);
}
function foo(salutation) {
alert(salutation + " " + this.name);
}
var t = new Thing('Joe');
t.doSomething(foo, 'Hi'); // Alerts "Hi Joe" via `foo`
Sometimes it's useful to pass the arguments you want to give the callback as an array, rather than individually. You can use apply to do that:
function Thing(name) {
this.name = name;
}
Thing.prototype.doSomething = function(callback) {
// Call our callback, but using our own instance as the context
callback.apply(this, ['Hi', 3, 2, 1]);
}
function foo(salutation, three, two, one) {
alert(salutation + " " + this.name + " - " + three + " " + two + " " + one);
}
var t = new Thing('Joe');
t.doSomething(foo); // Alerts "Hi Joe - 3 2 1" via `foo`

It is good practice to make sure the callback is an actual function before attempting to execute it:
if (callback && typeof(callback) === "function") {
callback();
}

My 2 cent. Same but different...
<script>
dosomething("blaha", function(){
alert("Yay just like jQuery callbacks!");
});
function dosomething(damsg, callback){
alert(damsg);
if(typeof callback == "function")
callback();
}
</script>

function loadData(callback) {
//execute other requirement
if(callback && typeof callback == "function"){
callback();
}
}
loadData(function(){
//execute callback
});

function callback(e){
return e;
}
var MyClass = {
method: function(args, callback){
console.log(args);
if(typeof callback == "function")
callback();
}
}
==============================================
MyClass.method("hello",function(){
console.log("world !");
});
==============================================
Result is:
hello world !

Some of the answers, while correct may be a little tricky to understand. Here is an example in layman's terms:
var users = ["Sam", "Ellie", "Bernie"];
function addUser(username, callback)
{
setTimeout(function()
{
users.push(username);
callback();
}, 200);
}
function getUsers()
{
setTimeout(function()
{
console.log(users);
}, 100);
}
addUser("Jake", getUsers);
The callback means, "Jake" is always added to the users before displaying the list of users with console.log.
Source (YouTube)

If you want to execute a function when something is done. One of a good solution is to listen to events.
For example, I'll implement a Dispatcher, a DispatcherEvent class with ES6,then:
let Notification = new Dispatcher()
Notification.on('Load data success', loadSuccessCallback)
const loadSuccessCallback = (data) =>{
...
}
//trigger a event whenever you got data by
Notification.dispatch('Load data success')
Dispatcher:
class Dispatcher{
constructor(){
this.events = {}
}
dispatch(eventName, data){
const event = this.events[eventName]
if(event){
event.fire(data)
}
}
//start listen event
on(eventName, callback){
let event = this.events[eventName]
if(!event){
event = new DispatcherEvent(eventName)
this.events[eventName] = event
}
event.registerCallback(callback)
}
//stop listen event
off(eventName, callback){
const event = this.events[eventName]
if(event){
delete this.events[eventName]
}
}
}
DispatcherEvent:
class DispatcherEvent{
constructor(eventName){
this.eventName = eventName
this.callbacks = []
}
registerCallback(callback){
this.callbacks.push(callback)
}
fire(data){
this.callbacks.forEach((callback=>{
callback(data)
}))
}
}
Happy coding!
p/s: My code is missing handle some error exceptions

When calling the callback function, we could use it like below:
consumingFunction(callbackFunctionName)
Example:
// Callback function only know the action,
// but don't know what's the data.
function callbackFunction(unknown) {
console.log(unknown);
}
// This is a consuming function.
function getInfo(thenCallback) {
// When we define the function we only know the data but not
// the action. The action will be deferred until excecuting.
var info = 'I know now';
if (typeof thenCallback === 'function') {
thenCallback(info);
}
}
// Start.
getInfo(callbackFunction); // I know now
This is the Codepend with full example.

function LoadData(callback)
{
alert('the data have been loaded');
callback(loadedData, currentObject);
}

function login(email, password, callback) {
//verify the user
const users = [
{ email: "abc#gmail.com", password: "123" },
{ email: "xyz#gmail.com", password: "xyz" }
];
const user = users.find(
(user) => user.email === email && user.password === password
);
callback(user);
`enter code here`}
function redirect(user) {
if (user) {
//user is successfully logged in
console.log("user is successfully logged in ");
} else {
console.log("Incorrect credentials ");
}
}
login("abc#gmail.com", "123", redirect);
I hope this example will help everyone who wants to know about the callback in JS

Try:
function LoadData (callback)
{
// ... Process whatever data
callback (loadedData, currentObject);
}
Functions are first class in JavaScript; you can just pass them around.

Related

Passing parameters to a callback in javascript/nodejs

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');

Test in which function a callback is executed

I'm trying to test in which function this callback function in executed. It should return a boolean value.
I hope you know what I mean.
Here the code example:
function test(par, callback) {
// ...
if (typeof callback == 'function') { // make sure the callback is a function
callback.call(this);
}
}
test("par", function() {
console.log("Test if in function test: " + "<if this is in function test>");
});
Is this similar to instanceof?
There's a non-standard way of doing it since the removal of arguments.caller
function test(par, callback) {
// ...
if (typeof callback == 'function') { // make sure the callback is a function
callback.call(this);
}
}
test("par", function cb() {
var isTestCaller = cb.caller === test;
console.log("Test if in function test: " + isTestCaller);
});
Another possible way doing it through error stacks (still non-standard):
var checkCaller = function(fnName) {
var e = new Error();
var caller = e.stack.split('\n')[2].trim().split(' ')[1];
return caller === fnName;
}
function wrapper(){
console.log(checkCaller('wrapper'));
}
wrapper();

Cancel a callback in javascript

I have something similar to this:
function MyObject() {
var self = this;
this.callback = function() {
self.finishParams = Array.prototype.slice.call(arguments);
self.parent.finish();
}
this.start = function() {
this.currentCallback = this.callback
this.startFunc.apply(this.startFunc, this.startParams.concat(this.currentCallback));
}
}
this.startFunc is a function which is something like function(param1, param2, param3, callback)
I have no control over this.startFunc except that it will call the callback with some paramaters.
THE PROBLEM
I have a this.currentCallback because I need to be able to cancel the callback. That is, I've already called this.startFunc and need to prevent the callback.
The problem is, MyObject might send another callback (never 2 at a time) but if I don't cancel the first one immediately when I need to, I won't know which one is valid when I get them back! Might be confusing so here's a diagram:
Send callback 1 off
Need to cancel! Cancel callback A somehow here
Send callback 2 off (still say function has callback 1)
By this point, if I didn't cancel A, then when I got the callback back, I wouldn't know which it was. If I DID cancel A, then I know it's B and no one has to worry.
Please tell me if you do not understand :)
A proof-of-concept of the scheme laid out in the comments: create a new closure for each callback, let callback identify if it is active or not.
function foreignAPIThatStartsACallback(callback) {
setTimeout(callback, 1000);
}
var activeCallback;
function wrapCallback(callback) {
var args = Array.prototype.slice.call(arguments, 1);
var that = this;
var wrappedCallback = function() {
if (wrappedCallback == activeCallback) {
callback.apply(that, args);
}
}
activeCallback = wrappedCallback;
return wrappedCallback;
}
function myCallback(what, who) {
console.log(who + " says " + what);
}
foreignAPIThatStartsACallback(wrapCallback(myCallback, "Hello", "Mario"));
foreignAPIThatStartsACallback(wrapCallback(myCallback, "Goodbye", "Luigi"));
// Mario is cancelled when Luigi gets started
With multiple possible actives:
function foreignAPIThatStartsACallback(callback) {
setTimeout(callback, 1000);
}
var activeCallbacks = {};
function wrapCallback(callback) {
if (!wrapCallback.count) wrapCallback.count = 0;
wrapCallback.count++;
var args = Array.prototype.slice.call(arguments, 1);
var that = this;
var wrappedCallback = function() {
if (wrappedCallback.id in activeCallbacks) {
cancelCallback(wrappedCallback);
callback.apply(that, args);
}
}
wrappedCallback.id = wrapCallback.count;
activeCallbacks[wrapCallback.count] = true;
return wrappedCallback;
}
function cancelCallback(wrappedCallback) {
delete activeCallbacks[wrappedCallback.id];
}
function myCallback(what, who) {
console.log(who + " says " + what);
}
var marioCallback = wrapCallback(myCallback, "Hello", "Mario");
foreignAPIThatStartsACallback(marioCallback);
var luigiCallback = wrapCallback(myCallback, "Goodbye", "Luigi");
foreignAPIThatStartsACallback(luigiCallback);
var daisyCallback = wrapCallback(myCallback, "Mama?", "Peach");
foreignAPIThatStartsACallback(daisyCallback);
cancelCallback(luigiCallback);
// Mario and Daisy go off

Multiple callback are needed

Suppose I have a script like this
var a = getData();
function getData() {
if (some_condition) { // Do this }
else {// Do this }
return fetchFromDB(param);
}
function fetchFromDB(param) {
db.transaction(function (tx) {
tx.executeSQL("SELECT foo FROM bar WHERE column = ? ", [param], function (tx, res) {
return res.rows.item(0).foo
});
});
}
Problem is tx.executeSQL is asynchronous and so fetchFromDB() method returns undefined to caller i.e getData(), same undefined is passed back to main function.
To avoid this I need to have one callback in fetchFromDB() like
tx.executeSQL("SELECT foo FROM bar WHERE column = ? ", [param], function (tx, res) {
callBack1(res.rows.item(0).foo);
});
So now value is returned to getData() but I need to have one callback2 here to return to main function also which returns data from callback1. By this way it seems that I need to have callback for where ever I called. Certainly I am missing something here ! How can I restrict to one callback ?
You can achieve this by using nested functions
function f1() {
function f1callback(param) {
console.log(" callback 1 " + param);
}
f2(f1callback);
}
function f2(callback) {
function f2callback(param) {
console.log(" callback 2 " + param);
callback(param);
}
f3(f2callback);
}
function f3(callback) {
function f3callback(param) {
console.log(" callback 3 " + param);
callback(param);
}
f4(f3callback);
}
function f4(callback) {
setTimeout(function () {
callback("data");
}, 1000);
}
f1();

JavaScript: Passing parameters to a callback function

I'm trying to pass some parameter to a function used as callback, how can I do that?
This is my try:
function tryMe(param1, param2) {
alert(param1 + " and " + param2);
}
function callbackTester(callback, param1, param2) {
callback(param1, param2);
}
callbackTester(tryMe, "hello", "goodbye");
If you want something slightly more general, you can use the arguments variable like so:
function tryMe(param1, param2) {
alert(param1 + " and " + param2);
}
function callbackTester(callback) {
callback(arguments[1], arguments[2]);
}
callbackTester(tryMe, "hello", "goodbye");
But otherwise, your example works fine (arguments[0] can be used in place of callback in the tester)
This would also work:
// callback function
function tryMe(param1, param2) {
alert(param1 + " and " + param2);
}
// callback executer
function callbackTester(callback) {
callback();
}
// test function
callbackTester(function() {
tryMe("hello", "goodbye");
});
Another Scenario :
// callback function
function tryMe(param1, param2, param3) {
alert(param1 + " and " + param2 + " " + param3);
}
// callback executer
function callbackTester(callback) {
//this is the more obivous scenario as we use callback function
//only when we have some missing value
//get this data from ajax or compute
var extraParam = "this data was missing";
//call the callback when we have the data
callback(extraParam);
}
// test function
callbackTester(function(k) {
tryMe("hello", "goodbye", k);
});
Your question is unclear. If you're asking how you can do this in a simpler way, you should take a look at the ECMAScript 5th edition method .bind(), which is a member of Function.prototype. Using it, you can do something like this:
function tryMe (param1, param2) {
alert (param1 + " and " + param2);
}
function callbackTester (callback) {
callback();
}
callbackTester(tryMe.bind(null, "hello", "goodbye"));
You can also use the following code, which adds the method if it isn't available in the current browser:
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function(){
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
Example
bind() - PrototypeJS Documentation
If you are not sure how many parameters are you going to be passed into callback functions, use apply function.
function tryMe (param1, param2) {
alert (param1 + " and " + param2);
}
function callbackTester(callback,params){
callback.apply(this,params);
}
callbackTester(tryMe,['hello','goodbye']);
When you have a callback that will be called by something other than your code with a specific number of params and you want to pass in additional params you can pass a wrapper function as the callback and inside the wrapper pass the additional param(s).
function login(accessedViaPopup) {
//pass FB.login a call back function wrapper that will accept the
//response param and then call my "real" callback with the additional param
FB.login(function(response){
fb_login_callback(response,accessedViaPopup);
});
}
//handles respone from fb login call
function fb_login_callback(response, accessedViaPopup) {
//do stuff
}
Wrap the 'child' function(s) being passed as/with arguments within function wrappers to prevent them being evaluated when the 'parent' function is called.
function outcome(){
return false;
}
function process(callbackSuccess, callbackFailure){
if ( outcome() )
callbackSuccess();
else
callbackFailure();
}
process(function(){alert("OKAY");},function(){alert("OOPS");})
Code from a question with any number of parameters and a callback context:
function SomeFunction(name) {
this.name = name;
}
function tryMe(param1, param2) {
console.log(this.name + ": " + param1 + " and " + param2);
}
function tryMeMore(param1, param2, param3) {
console.log(this.name + ": " + param1 + " and " + param2 + " and even " + param3);
}
function callbackTester(callback, callbackContext) {
callback.apply(callbackContext, Array.prototype.splice.call(arguments, 2));
}
callbackTester(tryMe, new SomeFunction("context1"), "hello", "goodbye");
callbackTester(tryMeMore, new SomeFunction("context2"), "hello", "goodbye", "hasta la vista");
// context1: hello and goodbye
// context2: hello and goodbye and even hasta la vista
Use curried function as in this simple example.
const BTN = document.querySelector('button')
const RES = document.querySelector('p')
const changeText = newText => () => {
RES.textContent = newText
}
BTN.addEventListener('click', changeText('Clicked!'))
<button>ClickMe</button>
<p>Not clicked<p>
Faced this recently, to get it(especially if the parent function has multiple arguments doing different stuffs not related to the callback , is to have the callback placed with its argument in an arrow function passed as an argument.
function tryMe(param1, param2) {
alert(param1 + " and " + param2);
}
function callbackTester(callback, someArg, AnotherArg) {
callback();
}
callbackTester(()=> tryMe("hello", "goodbye"), "someArg", "AnotherArg");
...or simply if you dont have multiple arguments doing other stuff.
function tryMe(param1, param2) {
alert(param1 + " and " + param2);
}
function callbackTester(callback) {
callback();
}
callbackTester(()=> tryMe("hello", "goodbye"));
A new version for the scenario where the callback will be called by some other function, not your own code, and you want to add additional parameters.
For example, let's pretend that you have a lot of nested calls with success and error callbacks. I will use angular promises for this example but any javascript code with callbacks would be the same for the purpose.
someObject.doSomething(param1, function(result1) {
console.log("Got result from doSomething: " + result1);
result.doSomethingElse(param2, function(result2) {
console.log("Got result from doSomethingElse: " + result2);
}, function(error2) {
console.log("Got error from doSomethingElse: " + error2);
});
}, function(error1) {
console.log("Got error from doSomething: " + error1);
});
Now you may want to unclutter your code by defining a function to log errors, keeping the origin of the error for debugging purposes. This is how you would proceed to refactor your code:
someObject.doSomething(param1, function (result1) {
console.log("Got result from doSomething: " + result1);
result.doSomethingElse(param2, function (result2) {
console.log("Got result from doSomethingElse: " + result2);
}, handleError.bind(null, "doSomethingElse"));
}, handleError.bind(null, "doSomething"));
/*
* Log errors, capturing the error of a callback and prepending an id
*/
var handleError = function (id, error) {
var id = id || "";
console.log("Got error from " + id + ": " + error);
};
The calling function will still add the error parameter after your callback function parameters.
Let me give you a very plain Node.js style example of using a callback:
/**
* Function expects these arguments:
* 2 numbers and a callback function(err, result)
*/
var myTest = function(arg1, arg2, callback) {
if (typeof arg1 !== "number") {
return callback('Arg 1 is not a number!', null); // Args: 1)Error, 2)No result
}
if (typeof arg2 !== "number") {
return callback('Arg 2 is not a number!', null); // Args: 1)Error, 2)No result
}
if (arg1 === arg2) {
// Do somethign complex here..
callback(null, 'Actions ended, arg1 was equal to arg2'); // Args: 1)No error, 2)Result
} else if (arg1 > arg2) {
// Do somethign complex here..
callback(null, 'Actions ended, arg1 was > from arg2'); // Args: 1)No error, 2)Result
} else {
// Do somethign else complex here..
callback(null, 'Actions ended, arg1 was < from arg2'); // Args: 1)No error, 2)Result
}
};
/**
* Call it this way:
* Third argument is an anonymous function with 2 args for error and result
*/
myTest(3, 6, function(err, result) {
var resultElement = document.getElementById("my_result");
if (err) {
resultElement.innerHTML = 'Error! ' + err;
resultElement.style.color = "red";
//throw err; // if you want
} else {
resultElement.innerHTML = 'Result: ' + result;
resultElement.style.color = "green";
}
});
and the HTML that will render the result:
<div id="my_result">
Result will come here!
</div>
You can play with it here: https://jsfiddle.net/q8gnvcts/ - for example try to pass string instead of number: myTest('some string', 6, function(err, result).. and see the result.
I hope this example helps because it represents the very basic idea of callback functions.
function tryMe(param1, param2) {
console.log(param1 + " and " + param2);
}
function tryMe2(param1) {
console.log(param1);
}
function callbackTester(callback, ...params) {
callback(...params);
}
callbackTester(tryMe, "hello", "goodbye");
callbackTester(tryMe2, "hello");
read more about the spread syntax
I'm trying to pass some parameter to a function used as callback, how can I do that?
I think he is implying that he wants to call the function this callbackTester(tryMe, "hello", "goodbye"). To do this we can use the Rest Operator (...). This operator takes the arguments that a function receives and dumps them into a real array that we will use to access in our callback function.
Now, some other developers might also argue that we could use the arguments "array". That will be fine, but we should be careful with it. arguments is not a real array but an array-like object with a length property.
Here is a working snippet using the Rest Operator:
function tryMe(params) {
console.log(params.join(', '));
}
function callbackTester(callback, ...params) {
callback(params);
}
callbackTester(tryMe, 'hello', 'goodbye', 'hi again');
callbackTester(tryMe, 'hello', 'goodbye');
callbackTester(tryMe, 'hello');
Just use the bind() function which is primarily used to set the this value. However, we can also use it to pass parameters without calling the function due to bind() returning a new function with the sequence of arguments provided.
Example:
function foo(param1, param2, param3) {
console.log(param1, param2, param3);
}
setTimeout(foo.bind(null, 'foo', 'bar', 'baz'), 1000);
In the snippet above, the setTimeout function takes 2 arguments, the callback function and a minimum time in ms for the function to be called, so when passing the callback function we're going to use bind and specify the parameters
Note: The first parameter of bind is the value that we want to set for this, and because we're not interested on that, null was passed, the subsequent parameters in bind are going to be the parameters for the callback.
I was looking for the same thing and end up with the solution and here it's a simple example if anybody wants to go through this.
var FA = function(data){
console.log("IN A:"+data)
FC(data,"LastName");
};
var FC = function(data,d2){
console.log("IN C:"+data,d2)
};
var FB = function(data){
console.log("IN B:"+data);
FA(data)
};
FB('FirstName')
Also posted on the other question here
//Suppose function not taking any parameter means just add the GetAlterConfirmation(function(result) {});
GetAlterConfirmation('test','messageText',function(result) {
alert(result);
}); //Function into document load or any other click event.
function GetAlterConfirmation(titleText, messageText, _callback){
bootbox.confirm({
title: titleText,
message: messageText,
buttons: {
cancel: {
label: '<i class="fa fa-times"></i> Cancel'
},
confirm: {
label: '<i class="fa fa-check"></i> Confirm'
}
},
callback: function (result) {
return _callback(result);
}
});

Categories