I recently started learning javascript to help maintain some stuff and ran into this issue today:
this.moveChar = function(){
// body here
setTimeout(moveChar,1000);
}
this.initialise= function(){
this.moveChar();
}
When initialise is called, I expected moveChar to be called, then repeated call itself once every 1000ms
However, what actually happens is moveChar gets called once then that's it. Based on other stackoverflow posts I read, I suspected it might be something to do with the function being expressed rather than declared. I have tried to use
this.moveChar = function recMove(){
// body here
setTimeout(recMove,1000);
}
without luck either.
Any suggestions on how I can fix this?
EDIT: Main thing I need to do is have the moveChar function called once every second. If there is a better approach than setTimeout recursion, I'm open to it
this.moveChar is not the same as moveChar, unless this is the global scope object like window.
this.moveChar is a property on an object, while moveChar would reference any variable in a visible scope chain.
You can change it to a couple of things in order to keep scope of whatever object is being used:
Using an arrow function
this.moveChar = function(){
// body here
setTimeout(()=>this.moveChar(),1000);
}
Using .bind()
this.moveChar = function(){
// body here
setTimeout(this.moveChar.bind(this),1000);
}
You might want to consider using setInterval() which is the more appropriate API for this task.
What setInterval() does is - it will repeatedly call the given function upon a certain interval is reached.
See:
https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval
Quote:
Repeatedly calls a function or executes a code snippet, with a fixed
time delay between each call. Returns an intervalID.
Example:
Assuming moveChar() contains your operation logic. Then to repeat it you'll do this 1 line.
let moveChar = function(){
// Do stuff
console.log("Hi thanks for calling me!");
}
setInterval(moveChar, 1000);
Are you using this in side body here?
If so, you should bind correct context while call.
this.moveChar = function(){
// body here
setTimeout(this.moveChar.bind(this), 1000);
}
Or use anonymous function:
this.moveChar = function(){
// body here
var that = this;
setTimeout(function(){
that.moveChar();
}, 1000);
}
Or arrow function:
this.moveChar = function(){
// body here
setTimeout(() => this.moveChar(), 1000);
}
Same notes apply to setInterval variant:
this.initialise= function(){
setInterval(this.moveChar.bind(this), 1000);
// var that = this;
// setInterval(function(){that.moveChar();}, 1000);
// setInterval(() => this.moveChar(), 1000);
}
this.moveChar = function(){
// body here
alert('called moveChar');
}
this.initialise= function(){
setInterval(function(){moveChar();},1000);
}
this.initialise();//call here
Related
My draw() function stops executing after calling this.circle(). If I call the draw() method directly, it works perfectly. However, if I use setInterval(this.draw, 1000), it seems to return right when calling this.circle(). circle() is never executed either. Am I misusing setInterval?
function Ball() {
this.start = function() {
return setInterval(this.draw, 1000);
}
this.circle = function() {
console.log('1');
}
this.draw = function() {
console.log('2');
this.circle();
console.log('3');
}
this.circle() is never executed, and console.long('3') (or anything after it) is never reached.
The result is console.long('2') being repeatedly printed every 1 second
When you use setInterval, this will refer to window. window doesn't have a function called draw, so it will throw an error and stop execution. To fix it, you need to bind the value of this.
return setInterval(this.draw.bind(this), 1000);
“this” is no longer what you think it is, and you have lost context to it, it no longer represents Ball like you’d like it to. You can bind your draw function like “this.draw.bind(this)” or you can capture “this” using a line like “const _this = this;” and then only reference “_this” in your functions to make sure you are always accessing the correct one.
Some reading on the closures
https://javascript.info/closure
As stated by #iagowp this will refer to the window when your method is called by setInterval. Instead, you can fix this by using an ES6 arrow function to call this.draw(). This way your this is referring to the Ball object rather than the function which called this.draw().
See working example below:
function Ball() {
this.start = function() {
return setInterval(_ => this.draw(), 1000);
}
this.circle = function() {
console.log('1');
}
this.draw = function() {
console.log('2');
this.circle();
console.log('3');
}
}
let b = new Ball();
b.start();
if (msg.content.toLowerCase() === "!start") {
var gov = setInterval(go, 1000);
var onev = setInterval(one, 1000);
var twov = setInterval(two, 1000);
function two(msg) {
msg.channel.send("https://i.imgur.com/JZOCg5l.png ");
}
function one(msg) {
msg.channel.send("https://i.imgur.com/gTK3Vhn.png ");
}
function go(msg) {
msg.channel.send("https://i.imgur.com/3iVfYIR.png ");
}
function two(msg) { }
function one(msg) { }
function go(msg) { }
msg.channel.sendFile("https://i.imgur.com/kOoyoZQ.png ").then(onev).then(twov).then(gov);
}
This is a very annoying task. I need to send these images about one second appart.
The current framework keeps giving me the following error:
C:\Users\maver\Documents\TestBot\testlev.js:197
msg.channel.sendFile("https://i.imgur.com/3iVfYIR.png ");
^
TypeError: Cannot read property 'channel' of undefined at Timeout.three [as _onTimeout]
(C:\Users\maver\Documents\TestBot\testlev.js:197:17)
at ontimeout (timers.js:478:11)
at tryOnTimeout (timers.js:302:5)
at Timer.listOnTimeout (timers.js:262:5)
I've tried this a multitude of different ways and am just about ready to throw in the towel.
Your syntax is slightly off. When you do function two(msg){... you are actually telling the function that you are going to pass it a new variable and that you want that variable called msg. Because of that, msg (in the context of your function) is undefined. You would have to pass in msg when you call the function from setInterval().
There are 2 ways you can bind msg to your function. The way that I personally like is this:
//...
var gov = setInterval(go.bind(null, msg), 1000);
var onev = setInterval(one.bind(null, msg), 1000);
var twov = setInterval(two.bind(null, msg), 1000);
//...
The .bind() function assigns the value of arguments. With the first argument of the function being called being the second argument of bind(). The first argument of bind() is what should be used as the value of this inside the function.
The other way to do this is with an anonymous function
//...
var gov = setInterval(function(){go(msg)}, 1000);
var onev = setInterval(function(){one(msg)}, 1000);
var twov = setInterval(function(){two(msg)}, 1000);
//...
Also note, setInterval() repeats a function call ever period. You may be looking for setTimeout() which would only fire the functions once after a delay.
When you use setInterval, you should know it will call the function, but will not provide any parameters to it (or even this). One way to fix it would be by using bind:
setInterval(go.bind(null, msg), 1000)
This would work, because bind() will create a new function where the parameters are "magically set in advance".
Another option in this case would be to simply not re-declare msg in the three functions - in that case, javascript will try to find msg from the outer scope, where it exists:
function two() {
msg.channel.send("https://i.imgur.com/JZOCg5l.png ");
}
Third, you shouldn't be using setInterval, but setTimeout, which will only call the function once.
The fourth problem you have is with timing. First, all three setTimeout calls happen at the same time, so all three functions will be called in one second (after 1000 millis). An easy fix would be simply:
setTimeout(go, 1000);
setTimeout(one, 2000);
setTimeout(two, 3000);
However, that will completely ignore how long it takes to send each message (which may or may not be what you want). If you wanted to wait a second after the previous message is sent, then you'd have to do something like:
msg.channel.sendFile("https://i.imgur.com/kOoyoZQ.png ").then(function() {
setTimeout(go, 1000);
});
function go() {
msg.channel.send("https://i.imgur.com/3iVfYIR.png").then(function() {
setTimeout(one, 1000);
});
}
// etc
That would be very tedious, as all the functions will look very similar. So a better approach would be to create a list of messages, then have a single function to send all of them:
var msgs = [
"https://i.imgur.com/kOoyoZQ.png",
"https://i.imgur.com/JZOCg5l.png",
"https://i.imgur.com/gTK3Vhn.png",
"https://i.imgur.com/3iVfYIR.png"
];
function sendMsgs(msgs, delay) {
if (msgs.length < 1) return; // we're done
var remain = msgs.slice(1);
var sendRemain = sendMsgs.bind(null, remain, delay);
msg.channel.send(msgs[0]).then(function() {
setTimeout(sendRemain, delay);
});
}
sendMsgs(msgs, 1000);
Your code is executed immediately because you have to maintain the value anf promises you are using is not correctly used.
You can do it as follows :
if (msg.content.toLowerCase() === "!start") {
var urls = ["https://i.imgur.com/kOoyoZQ.png",
"https://i.imgur.com/JZOCg5l.png",
"https://i.imgur.com/gTK3Vhn.png",
"https://i.imgur.com/3iVfYIR.png" ];
function gov(urls){
for(let k=0; k<urls.length;k++){
setTimeout(function() { msg.channel.send(k); },k*1000)
}
}
gov(urls);
}
I have the following code:
function fn($){
return function(){
innerFn = function(){
setTimeout(show, 1000);
};
show = function(){
$.alert("TEST");
}
}
}
But, after one second, when the function show is run, it says $ is undefined. How do I resolve this issue?
how to pass arguments to a function in setTimeout
setTimeout has a built in mechanism for adding params
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);
use it.
If you're going to use this - you should be careful. but that's another question.
There are a number of things at play here. The most important being that your setTimeout never gets called, since innerFn never gets called. This should do the trick.
function fn($){
return function(){
setTimeout(function(){
$.alert("TEST");
}, 1000);
}
}
fn(window)(); //triggers your alert after 1000ms
Your code makes no any sense, because nothing is called:
function fn($){
return function(){
innerFn = function(){
setTimeout(show, 1000);
};
show = function(){
$.alert("TEST");
}
}
}
Let's say I'm calling fn passing window, then a function is returned, that I can executed. But because this function is containing only function declaration - you also forget var so you pollute the global scope, that is bad - nothing is happen.
You'll need at least one function call inside, like:
function fn($){
return function(){
var innerFn = function(){
setTimeout(show, 1000);
};
var show = function(){
$.alert("TEST");
}
innerFn();
}
}
fn(window)();
And that will works. However, it's definitely redundant. You can just have:
function fn($){
return function(){
function show(){
$.alert("TEST");
}
setTimeout(show, 1000);
}
}
To obtain the same result. However, if you're goal is just bound an argument to setTimeout, you can use bind. You could use the 3rd parameter of setTimeout as the documentation says, but it seems not supported in IE for legacy reason.
So, an example with bind will looks like:
function show() {
this.alert('test');
}
setTimeout(show.bind(window), 1000);
Notice also that window is the global object by default, so usually you do not have to do that, just alert is enough. However, I suppose this is not your actual code, but just a mere test, as the alert's string says.
If you prefer having window as first parameter instead, and you're not interested in the context object this, you can do something like:
function show($) {
$.alert('test');
}
setTimeout(show.bind(null, window), 1000);
I have this code for a newsfeed that I want to use.
I want it to look kind of like this:
function News(){
//Load new comments every 5 sec
setTimeout((function(){
console.log(this); //Returns Object #News
this.loadNewsFeed();
}).call(this),5000);
this.loadNewsFeed = function(){
// Implementation here
}
}
The problem here is that it says the Object News doesn't have an method called loadNewsFeed!
I've already got it to work if I put the anonymous function outside the object News.
Like this:
var news = new News();
//Load new comments every 5 sec
(function loopNews(){
news.loadNewsFeed();
setTimeout(loopNews,5000);
})();
So how can I do this inside the object News?
This should work:
function News()
{
var self = this;
this.loadNewsFeed = function(){
// Implementation here
};
//Load new comments every 5 sec
setInterval(function handler() // setInterval for endless calls
{
console.log(self); //Returns Object #News
self.loadNewsFeed();
return handler;
}(), 5000);
}
Explanation:
call(this) invokes the handler directly - and returns undefined to setInterval which means that it's executed immediately but no handler is set.
The handler-function executes in global context so this is the window-object. The local variable self "injects" the current (and desired) this - as self.
Edit 2:
Now executes immediately and registers a handler.
I'm writing a JavaSCript class that has a method that recursively calls itself.
Scheduler.prototype.updateTimer = function () {
document.write( this._currentTime );
this._currentTime -= 1000;
// recursively calls itself
this._updateUITimerHandler = window.setTimeout( arguments.callee , 1000 );
}
Property description:
_currentTime: the currentTime of the timer in miliseconds.
_updateUITimerHandler: stores the reference so can be used later with clearTimeout().
my problem is where I'm using recursion with setTimeout(). I know setTimeout() will accept some string to execute, or a reference to a function. since this function is method of an object, I don't know how to call it from outside. so I used the second format of setTimeout() and passed in a reference to the method itself. but it does not work.
Try this:-
Scheduler.prototype.startTimer = function() {
var self = this;
function updateTimer() {
this._currentTime -= 1000;
self.hTimer = window.setTimeout(updateTimer, 1000)
self.tick()
}
this.hTimer = window.setTimeout(updateTimer, 1000)
}
Scheduler.prototype.stopTimer = function() {
if (this.hTimer != null) window.clearTimeout(this.hTimer)
this.hTimer = null;
}
Scheduler.prototype.tick = function() {
//Do stuff on timer update
}
Well the first thing to say is that if you're calling setTimeout but not changing the interval, you should be using setInterval.
edit (update from comment): you can keep a reference from the closure if used as a class and setInterval/clearInterval don't require re-referencing.
edit2: it's been pointed out that you wrote callee which will work quite correctly and 100% unambiguously.
Out of completeness, this works:
function f()
{
alert('foo');
window.setTimeout(arguments.callee,5000);
}
f();
so I tried out document.write instead of alert and that is what appears to be the problem. doc.write is fraught with problems like this because of opening and closing the DOM for writing, so perhaps what you needed is to change the innerHTML of your target rather than doc.write
You could hold a pointer towards it...
/* ... */
var func = arguments.callee;
this._updateUITimerHandler = window.setTimeout(function() { func(); }, 1000);
/* ... */