I have this self made function called succeeder which is supposed to try to run a function called func and if it fails to try and run it again after an interval.
This works great the first time and when func is called for the second time after setTimeout it fails and this seems out of context.
Can you think of anything that is not right in this snippet?
succeeder({
func : function () {
!this.dbOpen && this.init();
return true;
},
context : this,
interval : 2000,
success : function () { return true;}
});
function succeeder(opts) {
function run() {
try {
//_.delay(function(){opts.func();}.bind(opts.context), 2000);
setTimeout(function(){ return _.bind(opts.func, opts.context)(); }, 2000);
} catch (e) {
run(this.opts, this.interval);
}
}
run();
};
Your code doesn't do anything like the description. Also the description isn't clear: what does "if it [func] fails" mean? Does func throw an exception? Return false? Or what?
There is no code to check the return value from func, yet the example version of func returns true. What is the purpose of the true return value?
You have a try/catch block, which made me suspect you are talking about the func call throwing an exception. But this try/catch won't catch any exception that func throws!
That's because the try/catch is wrapping the setTimeout call. But func is not called from inside that try/catch block. It's called later, when the timeout fires, and the try/catch is no longer in effect at that time.
What is the opts.success function? It is never used.
Also, the code always takes a two-second delay before the first attempt to call func. Do you want that, or do you want the first call to be immediate and only take the delay if the call fails and you retry?
Here is a working example that does something like what you're talking about. I made the assumption that "fail" means "throw an exception", and that you don't want any delay on the initial func call.
To test the code I used a func function that runs a countdown and throws an exception each time and finally succeeds when the countdown reaches 0.
Run this with the debug console open so you can see the console.log() messages:
function Test() {
this.countdown = 5;
succeeder({
func: function () {
console.log(
this.constructor.name,
'countdown:',
this.countdown
);
if( this.countdown-- ) {
console.log( 'func throwing error' );
throw new Error( 'fail' );
}
console.log( 'func success!' );
},
context: this,
interval: 1000,
success: function () { return true; }
});
};
new Test;
function succeeder(opts) {
var func = _.bind( opts.func, opts.context );
function run() {
try {
func();
} catch (e) {
setTimeout( run, opts.interval );
}
}
run();
};
Related
I have a click event that is triggered from another place automatically for the first time. My problem is that it runs too soon, since the required variables are still being defined by Flash and web services. So right now I have:
(function ($) {
$(window).load(function(){
setTimeout(function(){
$('a.play').trigger("click");
}, 5000);
});
})(jQuery);
The problem is that 5 seconds for a person with a slow internet connection could be too fast and vice versa, for a person with a fast internet connection, it's too slow.
So how should I do the delay or timeout until someVariable is defined?
The following will keep looking for someVariable until it is found. It checks every 0.25 seconds.
function waitForElement(){
if(typeof someVariable !== "undefined"){
//variable exists, do what you want
}
else{
setTimeout(waitForElement, 250);
}
}
async, await implementation, improvement over #Toprak's answer
(async() => {
console.log("waiting for variable");
while(!window.hasOwnProperty("myVar")) // define the condition as you like
await new Promise(resolve => setTimeout(resolve, 1000));
console.log("variable is defined");
})();
console.log("above code doesn't block main function stack");
After revisiting the OP's question. There is actually a better way to implement what was intended: "variable set callback". Although the below code only works if the desired variable is encapsulated by an object (or window) instead of declared by let or var (I left the first answer because I was just doing improvement over existing answers without actually reading the original question):
let obj = encapsulatedObject || window;
Object.defineProperty(obj, "myVar", {
configurable: true,
set(v){
Object.defineProperty(obj, "myVar", {
configurable: true, enumerable: true, writable: true, value: v });
console.log("window.myVar is defined");
}
});
see Object.defineProperty
or use es6 proxy (which is probably overkill)
If you are looking for more:
/**
* combining the two as suggested by #Emmanuel Mahuni,
* and showing an alternative to handle defineProperty setter and getter
*/
let obj = {} || window;
(async() => {
let _foo = await new Promise(res => {
Object.defineProperty(obj, "foo", { set: res });
});
console.log("obj.foo is defined with value:", _foo);
})();
/*
IMPORTANT: note that obj.foo is still undefined
the reason is out of scope of this question/answer
take a research of Object.defineProperty to see more
*/
// TEST CODE
console.log("test start");
setTimeout(async () => {
console.log("about to assign obj.foo");
obj.foo = "Hello World!";
// try uncomment the following line and compare the output
// await new Promise(res => setTimeout(res));
console.log("finished assigning obj.foo");
console.log("value of obj.foo:", obj.foo); // undefined
// console: obj.foo is defined with value: Hello World!
}, 2000);
I would prefer this code:
function checkVariable() {
if (variableLoaded == true) {
// Here is your next action
}
}
setTimeout(checkVariable, 1000);
I prefer something simple like this:
function waitFor(variable, callback) {
var interval = setInterval(function() {
if (window[variable]) {
clearInterval(interval);
callback();
}
}, 200);
}
And then to use it with your example variable of someVariable:
waitFor('someVariable', function() {
// do something here now that someVariable is defined
});
Note that there are various tweaks you can do. In the above setInterval call, I've passed 200 as how often the interval function should run. There is also an inherent delay of that amount of time (~200ms) before the variable is checked for -- in some cases, it's nice to check for it right away so there is no delay.
With Ecma Script 2017 You can use async-await and while together to do that
And while will not crash or lock the program even variable never be true
//First define some delay function which is called from async function
function __delay__(timer) {
return new Promise(resolve => {
timer = timer || 2000;
setTimeout(function () {
resolve();
}, timer);
});
};
//Then Declare Some Variable Global or In Scope
//Depends on you
let Variable = false;
//And define what ever you want with async fuction
async function some() {
while (!Variable)
await __delay__(1000);
//...code here because when Variable = true this function will
};
////////////////////////////////////////////////////////////
//In Your Case
//1.Define Global Variable For Check Statement
//2.Convert function to async like below
var isContinue = false;
setTimeout(async function () {
//STOPT THE FUNCTION UNTIL CONDITION IS CORRECT
while (!isContinue)
await __delay__(1000);
//WHEN CONDITION IS CORRECT THEN TRIGGER WILL CLICKED
$('a.play').trigger("click");
}, 1);
/////////////////////////////////////////////////////////////
Also you don't have to use setTimeout in this case just make ready function asynchronous...
You can use this:
var refreshIntervalId = null;
refreshIntervalId = setInterval(checkIfVariableIsSet, 1000);
var checkIfVariableIsSet = function()
{
if(typeof someVariable !== 'undefined'){
$('a.play').trigger("click");
clearInterval(refreshIntervalId);
}
};
Here's an example where all the logic for waiting until the variable is set gets deferred to a function which then invokes a callback that does everything else the program needs to do - if you need to load variables before doing anything else, this feels like a neat-ish way to do it, so you're separating the variable loading from everything else, while still ensuring 'everything else' is essentially a callback.
var loadUser = function(everythingElse){
var interval = setInterval(function(){
if(typeof CurrentUser.name !== 'undefined'){
$scope.username = CurrentUser.name;
clearInterval(interval);
everythingElse();
}
},1);
};
loadUser(function(){
//everything else
});
Instead of using the windows load event use the ready event on the document.
$(document).ready(function(){[...]});
This should fire when everything in the DOM is ready to go, including media content fully loaded.
Shorter way:
var queue = function (args){
typeof variableToCheck !== "undefined"? doSomething(args) : setTimeout(function () {queue(args)}, 2000);
};
You can also pass arguments
I have upvoted #dnuttle's answer, but ended up using the following strategy:
// On doc ready for modern browsers
document.addEventListener('DOMContentLoaded', (e) => {
// Scope all logic related to what you want to achieve by using a function
const waitForMyFunction = () => {
// Use a timeout id to identify your process and purge it when it's no longer needed
let timeoutID;
// Check if your function is defined, in this case by checking its type
if (typeof myFunction === 'function') {
// We no longer need to wait, purge the timeout id
window.clearTimeout(timeoutID);
// 'myFunction' is defined, invoke it with parameters, if any
myFunction('param1', 'param2');
} else {
// 'myFunction' is undefined, try again in 0.25 secs
timeoutID = window.setTimeout(waitForMyFunction, 250);
}
};
// Initialize
waitForMyFunction();
});
It is tested and working! ;)
Gist: https://gist.github.com/dreamyguy/f319f0b2bffb1f812cf8b7cae4abb47c
Object.defineProperty(window, 'propertyName', {
set: value => {
this._value = value;
// someAction();
},
get: () => this._value
});
or even if you just want this property to be passed as an argument to a function and don't need it to be defined on a global object:
Object.defineProperty(window, 'propertyName', { set: value => someAction(value) })
However, since in your example you seem to want to perform an action upon creation of a node, I would suggest you take a look at MutationObservers.
I have an adaptation of the answer by #dnuttle that I would suggest using.
The advantage of using a try-catch block is that if any part of the code you are trying to execute fails, the whole block fails. I find this useful because it gives you a kind of transaction; everything or nothing gets done.
You should never write code that could end up in an endless loop due to external factors. This is exactly what would happen if you were waiting for a response from an ajax request and the server doesn't respond. I think it's good practice to have a timeout for any questionable loops.
let time = 0; // Used to keep track of how long the loop runs
function waitForElement() {
try {
// I'm testing for an element, but this condition can be
// any reasonable condition
if (document.getElementById('test') === null) {
throw 'error';
}
// This is where you can do something with your variable
// document.getElementById('test').addEventListener....
// or call a function that uses your value
} catch (error) {
// Loop stops executing if not successful within about 5 seconds
if (time > 5000) {
// I return false on failure so I can easily check for failure
return false;
} else {
// Increment the time and call the function again
time += 250;
setTimeout(waitForElement, 250);
}
}
}
// Call the function after the definition, ensures that time is set
waitForElement();
You could have Flash call the function when it's done. I'm not sure what you mean by web services. I assume you have JavaScript code calling web services via Ajax, in which case you would know when they terminate. In the worst case, you could do a looping setTimeout that would check every 100 ms or so.
And the check for whether or not a variable is defined can be just if (myVariable) or safer: if(typeof myVariable == "undefined")
Very late to the party but I want to supply a more modern solution to any future developers looking at this question. It's based off of Toprak's answer but simplified to make it clearer as to what is happening.
<div>Result: <span id="result"></span></div>
<script>
var output = null;
// Define an asynchronous function which will not block where it is called.
async function test(){
// Create a promise with the await operator which instructs the async function to wait for the promise to complete.
await new Promise(function(resolve, reject){
// Execute the code that needs to be completed.
// In this case it is a timeout that takes 2 seconds before returning a result.
setTimeout(function(){
// Just call resolve() with the result wherever the code completes.
resolve("success output");
}, 2000);
// Just for reference, an 'error' has been included.
// It has a chance to occur before resolve() is called in this case, but normally it would only be used when your code fails.
setTimeout(function(){
// Use reject() if your code isn't successful.
reject("error output");
}, Math.random() * 4000);
})
.then(function(result){
// The result variable comes from the first argument of resolve().
output = result;
})
.catch(function(error){
// The error variable comes from the first argument of reject().
// Catch will also catch any unexpected errors that occur during execution.
// In this case, the output variable will be set to either of those results.
if (error) output = error;
});
// Set the content of the result span to output after the promise returns.
document.querySelector("#result").innerHTML = output;
}
// Execute the test async function.
test();
// Executes immediately after test is called.
document.querySelector("#result").innerHTML = "nothing yet";
</script>
Here's the code without comments for easy visual understanding.
var output = null;
async function test(){
await new Promise(function(resolve, reject){
setTimeout(function(){
resolve("success output");
}, 2000);
setTimeout(function(){
reject("error output");
}, Math.random() * 4000);
})
.then(function(result){
output = result;
})
.catch(function(error){
if (error) output = error;
});
document.querySelector("#result").innerHTML = output;
}
test();
document.querySelector("#result").innerHTML = "nothing yet";
On a final note, according to MDN, Promises are supported on all modern browsers with Internet Explorer being the only exception. This compatibility information is also supported by caniuse. However with Bootstrap 5 dropping support for Internet Explorer, and the new Edge based on webkit, it is unlikely to be an issue for most developers.
while (typeof myVar == void(0)) {
if ( typeof myVar != void(0)) {
console.log(myVar);
}
}
This makes use of the typeof operator which only returns undefined if variable is not declared yet. Should be applicable in every type of javascript.
I'm having a synchronization and loading issues with some JS modules when the program starts. This error only shows up once at the beginning and then everything works, so it is an obvious sync problem.
The code:
//pyramid of doom
function initGame(){
initWorld(function(){
initPlayer(function(){
initBots(function(){
console.log("Game Loaded!");
update();
})
})
});
}
function initWorld(callback){
world.init(worldParams);
callback&&callback();
}
function initPlayer(callback){
player.init(scene,playerParams,world.getPhysicModel());
callback&&callback();
}
function initBots(callback){
bots.init(scene,botsParams,world.getPhysicModel());
callback&&callback();
}
function update() {
world.update(1/60);
player.update();
bots.update();
}
initGame();
The following is the error I'm getting.
Bots.js:112 Uncaught TypeError: Cannot read property 'mixer' of undefined
at Bots.update (Bots.js:112)
at update (Final.html:160)
What am I doing wrong? How can I synchronize the execution of the init functions?
(What I think that is going on is that the execution of initBots doesn't reach it end before the udpdate function starts to run.)
You can find the Bots.js module in my repository at ( 1 )
In bots.init you execute new THREE.ColladaLoader().load which looks to be asynchronous.
In its callback, you fill your _bots array (self._bots[modelLoaded] = bot;).
However, you execute bots.init() and do not wait for these asynchronous calls to complete before executing the initBots function callback. In the case of initGame execution, this callback executes update(), which in turn executes bots.update(), which tries to access this._bots[i].mixer with i index up to this._botsParams.length, i.e. a pre-defined value that does not account for how many items have been actually filled in _bots array.
Hence your error message: the array has no items yet at some indices, and trying to read a property on undefined throws an error.
Conclusion: common asynchronous issue.
You need to be passing the callbacks into the init functions. Guessing from a brief look at your bots code, they are not expecting to receive callbacks so you might be in for a rebuild.
You can't tell from the outside of an async function if it is done yet!
Equivalent to what you are doing:
let result = undefined
function takeTime () {
setTimeout(function() {
result = 'hello!'
}, 100)
}
function callback () {
console.log(result)
}
function run (callback) {
takeTime()
callback && callback()
}
run(callback) // undefined! takeTime has not finished yet
What you need to be doing:
let result = undefined
function takeTime (callback) {
setTimeout(function() {
result = 'hello!'
callback()
}, 100)
}
function callback () {
console.log(result)
}
function run (callback) {
callback && takeTime(callback)
}
run(callback) // 'hello!', the callback was only called once the takeTime function completed
i have group of bind's, which gets trigger inside a function from before and after the code. like following:
function global() {
before(); // call all before binds here
//... mainFunction code...
after(); // call all after binds here
}
if one of functions inside before(); callback wants to exit or stop the global() from running further, how can i stop it, without checking the return values?
The only way to achieve this without checking the value returned, is to raise an exception by throwing an error.
function before() {
throw new Error('Ending execution');
}
function after() {
console.log('Have you met Ted?');
}
function global() {
before();
// never reaches here
after();
}
global(); // Error: Ending execution
console.log('foo'); // not executed
If you have global invoked somewhere and want any code following it's invocation to continue being executed, you'll need to wrap it with a try..catch, for example
function global() {
try {
before();
// never reaches here
after();
} catch (e) {
console.log(e); // log error. Leave this block empty for no action
}
}
global(); // Error logged
console.log('bar'); // still executed
So in previous questions I've been told to call/execute/start functions like thisFunc; instead of thisFunc();.
And I've found that sometimes that works and sometimes it doesn't.
<script type='text/javascript'>
var valgo = 0;
var thing = "";
var lastPost = document.getElementById(<? echo $_SESSION['countything']; ?>);
lastPost.style.opacity = valgo;
function valgogoer(thing){
valgo += .05;
if (lastPost.style.opacity < 1){
lastPost.style.opacity = valgo;
}
}
setInterval(function(){valgogoer(<? echo $_SESSION['countything']; ?>)}, 50);
// Somethings are leftover from when I was messing with it, like the parameter thing.
</script>
In this code (and please tell me if it's awful), because I'm using setInterval to call a function with a parameter, I found through research it must be called the way it is above.
So two questions
When am I actually supposed to use () in calling functions?
In the code above, how can I make it so that it stops executing that function after the opacity hits 1. Currently it's restrained to 1, but it's still being called, and I've got a feeling it's better to stop the function being called, than to have it being called but not doing anything.
Thanks!
You use the brackets when you want to call the function. But if just wanna pass the content of the function, you do not. Examples:
var a = function(){
return "I'm a function";
}
var b = a;//Equals to function(){return "I'm a function";}
var c = a();//Equals to "I'm a function"
In event handlers, you do not use the brackets because you have to say to the browser to execute the content of the function. If you put them, the browser will call the return value of the function, which may cause an error:
var a = function(){
alert("Welcome to my site");
}
window.onload = a();//Wrong, equals to undefined, since the a function doesn't return any value
window.onload = a;//Correct, calls the function a when the event is fired
This same thing happens when you call the setInterval method with a function as the parameter. That's why the brackets are so important
You use thisFunc() when you want to call the function. You use thisFunc when you want a reference to the function as a value.
When your function has no parameter, you can use the reference for a callback:
function thisFunc() {
// do something
}
window.setTimeout(thisFunc, 1000);
When your function has a parameter, you need to wrap it in another function to call it with the parameter value:
function thisFunc(param1) {
// do something
}
window.setTimeout(function(){ thisFunc(42); }, 1000);
You can of course wrap a parameterless function in a function too:
function thisFunc() {
// do something
}
window.setTimeout(function(){ thisFunc(); }, 1000);
You don't need to use an anonymous function to wrap a function, you can use a named function and get the reference to that:
function thisFunc(param1) {
// do something
}
function callFunc() {
thisFunc(42);
}
window.setTimeout(callFunc, 1000);
You use () when you want another function to execute your function
function log(arg) { console.log(arg); }
setTimeout(log, 1000) // Logs undefined after 1 second
log("hi"); // logs the String hi
The function is reusable so you could actually use it yourself
function logUsingTheLogMethod( callback ) {
if ( typeof callback === "function" ) {
callback( "This will log to the console!" );
callback( log === callback ); // Logs true
}
}
logUsingTheLogMethod( log );
This is a common pattern in JS, to use functions as callbacks in methods
Say you had some functions that did math, but you don't want to write a logging method for all of them.
function add(a,b,fn) {
if ( fn === log ) {
fn( a + b );
}
}
function subtract(a,b,fn) {
if ( fn === log ) {
fn( a - b );
}
}
add(1, 2, log); // logs 3
subtract(5, 4, log) // logs 1
or modify the function so it ensures it's a function instead of the log function and you can do anything with the response
function add(a,b,fn) {
if ( typeof fn === "function" ) {
fn( a + b );
}
}
// answer is automatically passed in by the calling add method
add( a, b, function ( answer ) {
// do ssomething with the answer
alert( answer );
});
This is a typical situation in node.js:
asyncFunction(arguments, callback);
When asynFunction completes, callback gets called. A problem I see with this pattern is that, if asyncFunction never completes (and asynFunction doesn't have a built-in time-out system) then callback will never be called. Worse, it seems that callback has no way of determining that asynFunction will never return.
I want to implement a "timeout" whereby if callback has not been called by asyncFunction within 1 second, then callback automatically gets called with the assumption that asynFunction has errored out. What is the standard way of doing this?
I'm not familiar with any libraries that do this, but it's not hard to wire up yourself.
// Setup the timeout handler
var timeoutProtect = setTimeout(function() {
// Clear the local timer variable, indicating the timeout has been triggered.
timeoutProtect = null;
// Execute the callback with an error argument.
callback({error:'async timed out'});
}, 5000);
// Call the async function
asyncFunction(arguments, function() {
// Proceed only if the timeout handler has not yet fired.
if (timeoutProtect) {
// Clear the scheduled timeout handler
clearTimeout(timeoutProtect);
// Run the real callback.
callback();
}
});
You probably need to come out with a solution of your own. Like
function callBackWithATimeout (callback, timeout) {
var run, timer;
run = function () {
if (timer) {
clearTimeout(timer);
timer = null;
callback.apply(this, arguments);
}
};
timer = setTimeout(run, timeout, "timeout");
return run;
}
and then
asyncFunction(arguments, callBackWithATimeout(callback, 2000));
You could do something like this:
function ensureExecution(func, timeout) {
var timer, run, called = false;
run = function() {
if(!called) {
clearTimeout(timer);
called = true;
func.apply(this, arguments);
}
};
timer = setTimeout(run, timeout);
return run;
}
Usage:
asyncFunction(arguments, ensureExecution(callback, 1000));
DEMO
But note the following:
The timeout is started immediately when you call ensureExecution, so you cannot cache that function reference.
The arguments passed to the callback will differ. For example asyncFunction might pass some arguments to callback upon success, but if the function is called by the timeout, no arguments will be passed. You have to keep that it mind. You could also provide default arguments with which the function should be called in this case:
function ensureExecution(func, timeout, args, this_obj) {
// ...
timer = setTimeout(function() {
run.apply(this_obj, args);
}, timeout);
//...
}
I ran into the same problem with a content script trying to open the port on the BG extension before the BG extension was ready. A work around was to wait for the BG extension to reply to a message and repeat this till successful. Here are the code snippets.
Content Script:
var nTimes = 10;
var bIsReady = false;
checkBGReady();
function checkBGReady() {
if (!bIsReady) {
chrome.runtime.sendMessage({msgText: "hello "+nTimes}, function(response) {
if (response && response.ack) {
console.log("have response:"+response.ack+" "+nTimes);
bIsReady = true;
// continue with initialization
bootStrap(sURL);
checkReady();
} else {
console.log("have no ack response %o",response);
}
});
}
nTimes -= 1;
if (nTimes > 0 && !bIsReady) {
setTimeout(checkBGReady,100);
}
}
BG Extension
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(sender.tab ?"from a content script:" + sender.tab.url :"from the extension");
if (request.msgText) {
console.log("Have msg "+request.msgText);
sendResponse({ack: "have contact "+request.msgText});
}
});
In my case it usually took after the first 100ms delay.