I would finally like to reach some clarity on this aspect of programming, being basically self-taught. It's to do with passing variables around between functions.
With the code below, data.roomId is not recognized in the first function (obviously). I could place the function inside the joinRoom function to make it recognize data.roomId, but then how does the leaveRoom function recognize sendHeartbeat?
`io.on('connection', function (socket) {
function sendHeartbeat(){
setTimeout(sendHeartbeat, 8000);
socket.broadcast.to(data.roomId).emit('ping', { beat : 1 });
}
socket.on('joinRoom', function (data) {
socket.join(data.roomId)
setTimeout(sendHeartbeat, 8000);
});
socket.on('leaveRoom', function (data) {
socket.leave(data.roomId)
clearTimeout(sendHeartbeat)
});
});`
You can do what we call a closure, it is a function that returns a function keeping track of local variables. It is a cool concept !
function sendHeartbeat(roomId){
return function() {
socket.broadcast.to(roomId).emit('ping', { beat : 1 });
}
}
socket.on('joinRoom', function (data) {
socket.join(data.roomId)
setTimeout(sendHeartbeat(data.roomId), 8000);
});
You call the function sendHeartbeat passing it the roomId, and it returns the function you want to be executed in your setTimeout :)
Related
Is it possible to pass a callback function that does not exist yet? My goal is to have a common function that will wait for another callback function to exist, when it does exist, it should execute it. This is what I have so far, but I can't figure out how to pass the function in that doesn't exist as a function yet.
function RunTemplateFunction(callback, userInfo) {
if ($.isFunction(callback)) {
callback(userInfo);
} else {
var myInterval = setInterval(function () {
if ($.isFunction(callback)) {
clearInterval(myInterval);
callback(userInfo);
}
}, 200);
}
}
I run the function like this:
RunTemplateFunction(MyFunctionToRun, GetUserInfo());
I get MyFunctionToRun is undefined for obvious reasons, I also tried the workaround of passing the function as a string and then convert the string to a function using eval(). But that throws the same error. I also thought of using the new function(), but that actually creates a new function.
Any help is appreciated. thank you.
If you call RunTemplateFunction by undefined there is no way we can see, is callback is defined or not, as we don't have reference to anything.
If you can modify the declaration to accept object as below, we can achieve what we want
function RunTemplateFunction(options, userInfo) {
if ($.isFunction(options.callback)) {
console.log('called1',userInfo);
options.callback(userInfo);
} else {
var myInterval = setInterval(function () {
if ($.isFunction(options.callback)) {
console.log('Called dynamically!!');
clearInterval(myInterval);
options.callback(userInfo);
}
}, 200);
}
}
var options = {}
RunTemplateFunction(options,{user:122});
options.callback = function(){
console.log("I'm called!!");
}
This will print
Called dynamically!!
I'm called!!
EDIT:
We can also call callback function in following way without setInterval, it will look different but options.callback variable is replaced by template.callMe function and its instantaneous also.
function TemplateRunner(userInfo){
this.callMe = function(cb){
this.templateFunction(cb);
}
this.templateFunction = function(callback){
callback(userInfo);
}
}
var template = new TemplateRunner({user:100})
template.callMe(function(user){
console.log('call me1',user);
});
template.callMe(function(user){
console.log('call me2',user);
})
This will print
call me1 {user: 100}
call me2 {user: 100}
I am new to node.js and came across this bit of code and am trying to understand what the word "callback" is doing. I mean I know what a callback is, but why are they actually using the word "callback" below and what is it doing. Really appreciate your help.
io.sockets.on('connection', function(socket) {
socket.on('nickname', function(data, callback) {
if (nicknames.indexOf(data) != -1) {
callback(false);
} else {
callback(true);
nicknames.push(data);
socket.nickname = data;
console.log('Nicknames are ' + nicknames);
}
});
});
It's a variable name.
It's defined in the function expression as an argument: function (data, callback) {.
The expectation is that the value passed to it will be a function (hence the function being called here: callback(false);).
For a simple example that doesn't depend on any library code you can't see:
function doSomething(callback) {
alert("doing something");
callback(1);
}
function myCallback(value) {
alert("myCallback called with the argument: " + value);
}
doSomething(myCallback);
But function (data, bob) is the callback function. Why include "callback" (or bob) within the callback. Does that make sense?
You have multiple callback functions.
One callback function is passed another function as an argument which it, in turn, calls.
Node.js uses asynchronous callback functions instead of blocking the caller and performing the operation synchronously. callback is simply the very common name given to the variable that holds the callback function. I usually name the variable done in my own code simply because it is shorter.
In Node.js, callback is the de-facto standard name for callback functions. You could easily call it bob, and it would have the same effect:
io.sockets.on('connection', function(socket) {
socket.on('nickname', function(data, bob) {
if (nicknames.indexOf(data) != -1) {
bob(false);
} else {
bob(true);
nicknames.push(data);
socket.nickname = data;
console.log('Nicknames are ' + nicknames);
}
});
});
As to what it's doing, think of it as an anonymous function that notifies the caller (like a parent function) that the called function is done. A simpler example would be:
Function A calls function B (which takes a long time to run). When B is done, it needs to tell A that it's done:
function a() {
b(someParameter, function(err, data) {
// function(err, data) is the anonymous function we pass as a parameter to b()
});
}
function b(someParemeter, callback) {
// do something that takes a while
// 'callback' is the variable name for the anonymous function that was passed
// We execute it using ():
callback(err, data);
}
Assuming that this is socket.io, that callback is used to respond to a client when the 'nickname' event is triggered.
From their documentation
Sending and getting data (acknowledgements)
Sometimes, you might want to get a callback when the client confirmed
the message reception.
To do this, simply pass a function as the last parameter of .send or
.emit. What’s more, when you use .emit, the acknowledgement is done by
you, which means you can also pass data along:
Server (app.js)
var io = require('socket.io')(80);
io.on('connection', function (socket) {
socket.on('ferret', function (name, fn) {
fn('woot');
});
});
Client (index.html)
<script>
var socket = io(); // TIP: io() with no args does auto-discovery
socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
socket.emit('ferret', 'tobi', function (data) {
console.log(data); // data will be 'woot'
});
});
</script>
how to run next function after first done with setInterval?
for example:
step1();
step2();
setInterval(step1, 1000).done(function() {
setInterval(step2, 1000).done( /* next step */);
});
please help me with solution!
Edit: This is an old answer. Now you can achieve this using promises also but the code will be slightly different.
If you don't want to use a promise you can use a simple flag to achieve such a thing. Please see example below:
var flag = true;
function step1() {
console.log('title');
}
function step2() {
console.log('subtitle');
}
function wrapper() {
if(flag) {
step1();
} else {
step2();
}
flag = !flag;
}
setInterval(wrapper, 30000);
If you want to chain functions on completion you can use callback functions.
Example:
function first(callback) {
console.log('Running first');
if (callback) {
callback();
}
}
function second() {
console.log('Running second function');
}
first(second);
The first function checks if a callback is used and then runs it. If there is no callback function nothing happens. You can chain functions this way.
You can also use anonymous functions.
first(function () {
console.log('This function that will run after the first one);
});
If you use setTimeout() you can't be sure whether the previous function has completed. A better way would be to use promises.
Understanding Promises
I hope I understood your question right. Good luck!
First of all setInterval can not be done by itself, it will fire infinitely if you not clear it with clearInterval.
But if you have some async action inside your function and whant to wait for it and then call another function you may just promisify it like Avraam Mavridis suggested.
function step1() {
var deferred = $.Deferred();
setTimeout(function () {
alert('I am step 1');
deferred.resolve();
}, 1000);
return deferred.promise();
}
function step2() {
alert('I am step 2');
}
step1().done(step2);
JsFiddle
so I come from a heavy python background, and I'm trying to wrap my head around javascript. Here I have a function that returns an array of track IDs for soundcloud songs by the artist 'v-2-followers'. How would I go about assigning the output of SC.get(stuff) to a variable to reuse the track list in another function. I'm sure I'm missing something fundamental. I'm less looking for an answer that explains how to do this, but more why it's done like that.
That said I would also very much appreciate the how. :)
(function() {
SC.initialize({
client_id:'__CLIENTID__';
});
// Would like to set a variable equal to the output of
SC.get('/tracks', { q: 'v-2-followers' }, function(tracks) {
trackIdList = [];
tracks.forEach(function(track){
trackIdList.push(track.id);
});
return trackIdList;
});
// And use the variable here.
SC.stream('/tracks/'+trackIdList[Math.floor(Math.random() * myArray.length)], function(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function(e){
sound.resume();
}, function(e){
sound.pause();
});
});
})();
I can see that I'm missing something fundamental about variable assignment and scope, or function callbacks here. I've exhausted myself skimming docs on the subject. If anyone can tell me how to do this, and more importantly, why it's done that way, for future reference.
You have trackIdList as a global variable because it is not created using var. So as it is, you can already access it from any other function. If you wanted to limit its scope to just the outer function, add var trackIdList; as the first line of your function. You should be declaring variables with var everywhere in order to limit their scope.
(function() {
var trackIdList;
...
})();
Further reading: What is the scope of variables in JavaScript?
The other concept you need to understand is regarding asynchronous execution and callbacks in JavaScript. Your code that populates trackIdList is contained within a callback function which is (most likely) called after your call to SC.stream(). If SC.stream() depends on the value of trackIdList, it should be called from the callback function.
It may help to illustrate what's going on by separating out your callback functions.
(function () {
var trackIdList = [];
SC.initialize({
client_id: '__CLIENTID__'
});
SC.get('/tracks', { q: 'v-2-followers' }, processTracks);
var randomIndex = Math.floor(Math.random() * myArray.length);
SC.stream('/tracks/' + trackIdList[randomIndex], processSound);
function processTracks(tracks) {
tracks.forEach(function (track) {
trackIdList.push(track.id);
});
}
function processSound(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function (e) {
sound.resume();
}, function (e) {
sound.pause();
});
}
})();
SC.get() makes an asynchronous request and returns immediately. Then SC.stream() is called without waiting for the request to return. processTracks() isn't called until the request comes back. The trouble is that SC.stream() depends on processTracks(), but is called immediately. To fix this, call SC.stream() from the callback function of SC.get():
(function () {
SC.initialize({
client_id: '__CLIENTID__'
});
SC.get('/tracks', { q: 'v-2-followers' }, processTracks);
function processTracks(tracks) {
var trackIdList = [];
tracks.forEach(function (track) {
trackIdList.push(track.id);
});
var randomIndex = Math.floor(Math.random() * myArray.length);
SC.stream('/tracks/' + trackIdList[randomIndex], processSound);
}
function processSound(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function (e) {
sound.resume();
}, function (e) {
sound.pause();
});
}
})();
I'll explain one way - with callbacks. The reason people do it this way is that there are synchronous operations, and asynchronous operations. In your case, you need to perform an AJAX request - we don't know how long it will take for SC.get to finish, and we don't want the program to hang while we wait for it. So instead of waiting, we tell it "go get those tracks, and I'm passing you a function to call when you are done. In the meantime, I'm going to go on ahead with the rest of the program."
(function() {
SC.initialize({
client_id:'__CLIENTID__'
});
var getTracks = function(callback) {
SC.get('/tracks', { q: 'v-2-followers' }, function(tracks) {
trackIdList = [];
tracks.forEach(function(track){
trackIdList.push(track.id);
});
callback(trackIdList);
});
}
// And use the variable here.
var stream = function(trackIdList) {
SC.stream('/tracks/'+trackIdList[Math.floor(Math.random() * myArray.length)], function(sound) {
sound.play();
sound.pause();
$('#fabrizio').hover(function(e){
sound.resume();
}, function(e){
sound.pause();
});
});
}
getTracks(stream);
})();
Why is this code working:
function onCordovaReady() {
navigator.globalization.getLocaleName(function (locale) {
jQuery.i18n.properties({
name:'message',
path:'lang/',
mode:'map',
language:locale.value,
callback: function(){
alert(locale.value);
alert(jQuery.i18n.prop('msg_hello'));
alert(jQuery.i18n.prop('msg_complex', 'John'));
}
});
});
}
And this one not:
function onCordovaReady() {
navigator.globalization.getLocaleName(function (locale) {
jQuery.i18n.properties({
name:'message',
path:'lang/',
mode:'map',
language:locale.value,
callback: onLanguageReady(locale)
});
});
}
function onLanguageReady(locale) {
alert(locale.value);
alert(jQuery.i18n.prop('msg_hello'));
alert(jQuery.i18n.prop('msg_complex', 'John'));
}
I want to make the callback in a different function so my code will look cleaner, but couldn't get it to work. The first alert will work (it will display nl_NL), but the second and third alert will output [msg_hello] and [msg_complex].
Many thanks!
Try with this:
// beginning of code omitted
callback: function(locale) {
onLanguageReady(locale)
}
it is because you are assigning undefined to the callback property.
You are calling onLanguageReady and assigns that value to the callback method.
The solution is to use another function as callback function which will call the onLanguageReady function as given by #romainberger
function onCordovaReady() {
navigator.globalization.getLocaleName(function (locale) {
jQuery.i18n.properties({
name:'message',
path:'lang/',
mode:'map',
language:locale.value,
callback: onLanguageReady
});
});
}
function onLanguageReady(locale) {
alert(locale.value);
alert(jQuery.i18n.prop('msg_hello'));
alert(jQuery.i18n.prop('msg_complex', 'John'));
}
will work if the function calls back with locale.
the callback is expecting a function pointer that it can call once the processing is done when you say onLanguageReady(locale) you are actually executing the function and thus assigning the result of the function as the call back in this case the return is nothing thus undefined