I'm new to javascript and have been following this tutorial series on how sockets work: https://www.youtube.com/watch?v=HZWmrt3Jy10.
He writes:
var io = socket(server);
io.sockets.on('connection', newConnection);
function newConnection(socket){
console.log('new connection' + socket.id);
}
How does newConnection work in the sockets.on? It needs a socket parameter yet it works, it is a reference to the function as well. I have heard of callbacks vaguely but am still confused. Can someone please explain what is happening.
Functions in Javascript are "first class" objects and they can be passed around, stored in variables etc... consider this simple example:
function foo(x) {
console.log("foo(" + x + ") called");
}
function bar(f) {
f(42);
}
bar(foo);
last line calls the function bar passing it as a parameter the function foo. The function bar then calls the function f that it received passing 42 as argument.
The net result will be the message foo(42) called appearing in console.
In the code you show what happens is that the on function will store away the argument to call it later (providing the socket as parameter) once a connection is established.
You are passing your function so the socket.on method just like you would be passing a variable. And just like a variable, the code inside socket.on can then use your function including passing you the required parameter.
The socket.on() method would be implemented something like this:
Socket.prototype.on = function (event, callback) {
// other logic
callback(socket); // pass socket to your newConnection function
}
So when you call:
io.sockets.on('connection', newConnection);
you are passing your function newConnection as a callback (this can be named anything by the programmer who wrote socket.on) and then it will call your function as:
callback(socket); // callback is second parameter so
// in your case it is pointing to
// newConnection
so it passes the socket to your function.
Related
One of my functions in the frontend is using ‘this’.
function myFunction() {
...
console.log(this);
...
}
I call it like this:
myFunction.call(objAsThis, param1, param2);
and everything is working as expected. But then I tried to pass this function to the backend(node.js) as a callback using socket.io:
Client
socket.emit("some event", param3, param4, myFunction);
Calling this function in node in the same way rendered some unexpected results.
Server
socket.on("some event", function(param3, param4, myCbFunction) {
...
myCbFunction.call(someObject, param5, param6);
...
});
I expect to see the someObject object printed but instead I get the socket.io's ‘this’ printed. Same story if I try to use bind:
myCbFunction.bind(someObject)(param5, param6);
I even tried(out of curiosity) calling it with new, which should have created a new object, but it didn't. Outcome remained as before.
new myCbFunction(param5, param6);
I get the same result(socket.io's this) if I just console.log(this) inside the “some event” event handler on the server.
So myCbFunction completely ignored the call, the bind or the new.
Can someone please explain what is going on? Why it's not binding this to the object I provide?
OK, I think I see a source of confusion.
When you register this on the server:
socket.on("some event", function(param3, param4, callback) {
...
callback(someArgs);
});
The point of callback() is to notify the client that you have received the message. It's referred to in the documentation as an ack function. This is NOT directly calling your client code. In fact, this callback function is something that socket.io provides and when you call it you're calling socket.io, not calling the client.
Instead, you call this function provided by socket.io and when socket.io receives this function call it packages up a message and sends that message back to the client over the socket.io connection. You call that ack function on the server when you want to tell the client that you've received the original message.
Keep in mind that client and server are nearly always on separate computers miles apart (always at oppositive ends of a TCP socket). You can't directly call client code from the server.
The client socket.io code will receive that ack message (and any serializable arguments you sent with it) and then will call the client side ack function that you specified here:
socket.emit("some event", param3, param4, myFunction);
You do not control the this value when this ack function is called - it will be whatever socket.io sets it to (unless you declare it as an arrow function). But, instead of passing your actual function, you can use a stub function and then reattach the desired this value before calling your real function.
socket.emit("some event", param3, param4, function(...args) {
myFunction.call(objAsThis, ...args)
});
This will then ignore the this value that the socket.io library passed to the callback and will attach the one you want. You could also use .bind() to do the same thing:
socket.emit("some event", param3, param4, myFunction.bind(objAsThis));
which is just another way of passing a stub function as shown in the prior code solution.
And, if this was in the lexical context, you could also use an arrow function.
My questions here is about the way the call back function works.
const fs = require('fs');
let fileContents = 'initial value';
fs.readFile('file.txt', 'utf-8',function(error,res){
fileContents = res;
console.log(fileContents);
})
So, when the fs.readFile runs, the function(error,res) is called. But why does the fileContents receives the text inside the txt file if my parameter is empty?
I'm assuming that the readFile adds the value read to the res parameter.
Is it always like this?
Another questions is why do I get null when I erase error?
Readfile looks like something like this:
function readFile(path, cb) {
try {
// Some very complicated stuff
// All fine, call callback:
path(/*error:*/ null, /*res: */ "somedata");
} catch(error) {
path(error, /*res: */ undefined);
}
}
So what you get inside the callbacks parameter does not depend on its name, but on its position, so when you do:
readFile("/..", function(res) { /*...*/ });
res will be the error readFile passes back, and if thats null its a good thing.
Maybe take a little time to experiment with callback functions.
A callback function is just a function that you pass as a parameter to another function. In the code below I declared my function. myFunc uses another function as parameter to the function called callback. Inside my function I invoke the function and pass myName as parameter to the callback. This allows me to declare other anonymous functions as parameters which I included as examples. When myFunc is invoked it invokes the callback inside its local environment.
I can then manipulate the data that is passed to the callback and write my own code inside the anonymous function using the variable that is passed to myFuncs callback.
In your example you are using readFile which retrieves the data from the file and passes it to a callback function and/or passes an error assuming something went wrong.
function myFunc( callback){
let myName = "Joe Smo";
callback(myName);
}
/*
Now I can use my function and add another anonymous function to do whatever I want
provided I use the same parameters that are inside my function.
*/
myFunc(function(name){
console.log("What's up!"+name);
});
myFunc(function(strangeName){
console.log(strangeName+" is a strange name.");
});
I found the following example in ngResource documentation:
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
As I can understand, the second parameter of function query() is a function, which evaluated on success result of resource query. But simultaneously, this function takes the variable cards which is assigned from result of function query().
I can't understand, if this is normal to Javascript, since every async operation executes single thread?
Or special efforts were taken by creators of AngularJS in order to have function paramater executed after it's result returned?
How would I write my own function
function myfunction(argument, runbefore, runafter) {
runbefore();
POSTPONE runafter();
return Math.sin(argument);
}
which would execute 2nd parameter before itself and 3rd parameter -- after itself?
If I understand right, you are asking how it is possible for the callback function to be called after the return statement. One way that this is possible is through builtin functions that call another function at a later time. Take this code for example:
function doItLater(arg1, callbackFn) {
setTimeout(1000, callbackFn);
return arg1;
}
This will return the same argument that it was passed, and the callback function will be called later (about 1 second after the function has already returned). There are other ways a callback function can be delayed. For example, with an XMLHttpRequest, a callback function can be called after an HTTP response has been received. You can also connect to user events, so that a function will be called when the user does something specific.
If you want a little clarification on how things like setTimeout work in a single-threaded environment, I would suggest reading this article by John Resig.
I am trying to understand a code from socket.io/examples/chat/app.js.
I am not able to explain what the fn() calls mean in the lines 71 and 73.
I guess it is a callback function but where is its definition?
Is it a short invocation of another function from app.js ?...
Is it a call of a prototype function of the socket object?
The fragment is (lines 69-78):
[...]
socket.on('nickname', function (nick, fn) {
if (nicknames[nick]) {
fn(true);
} else {
fn(false);
nicknames[nick] = socket.nickname = nick;
socket.broadcast.emit('announcement', nick + ' connected');
io.sockets.emit('nicknames', nicknames);
}
});
[...]
fn is passed in as an argument to the callback function, right here:
socket.on('nickname', function (nick, fn) {
^^
Since JavaScript functions are objects, they can stored in variables — and passed as arguments into other functions.
The use of the callback function in this particular case is duscussed in the “Getting acknowledgements” section of the Socket.IO docs — calling it with some data sends a message back to the client as a response to that message.
fn is a reference to another function that is being called from within the on nickname event.
IN javascript, functions are treated as objects so they can pass as an argument with in a callback function.
I'm trying to build an API in JS that will perform some operations and then execute the callback that's registered in AS when it's done. Because it's an API, I am just providing a JS method signature for another developer to call in Flash. Thus, the callback name that's registered in the AS part of the code should be a parameter that's passed in to the JS API in order for JS to communicate back to Flash.
For example:
[AS3 code]
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", flashCallbackName);
function processRequest(data:String):void
{
//do stuff
}
[JS code]
var namespace =
{
jsFnToCall: function(callback)
{
//Do stuff in this function and then fire the callback when done.
//getFlashMovie is just a util function that grabs the
//Flash element via the DOM; assume "flash_id"'s a global var
//Below does not work...it's what I'd be ideally be doing some how.
getFlashMovie(flash_id).callback(data);
}
};
Because the definition of the function is in AS, I can't use the window[function name] approach. The only way I can think of is to build the callback in a string and then use the eval() to execute it.
Suggestions? T.I.A.
Well, I can think of one thing I would try, and one thing that would work.
What I would try first.
getFlashMovie(flash_id)['callback'](data);
What would work: Have the callback always be the same, say callback. The first parameter to the callback could be used to determine what actual function to call in flash. For example:
function callback($fn:String, $data:*) {
// either
this[$fn]($data);
// or
switch ($fn) {
case "callback1":
DoSomeCallback($data);
break;
}
Additionally passing the objectID makes it a bit simpler:
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", ExternalInterface.objectID, "flashCallbackName");
Then in your JS:
var namespace =
{
jsFnToCall: function(objectID, callback)
{
//Do stuff in this function and then fire the callback when done.
document[objectID][callback](data);
}
};