How to listen callback JS? - javascript

I have callback in interface:
interface {
onLoad?: () => void;
}
I tried to catch this callback using this:
props.onLoad(() => this.mapLoaded = true);
But I get this error:
Expected 0 arguments, but got 1.

It's hard to say for certain without more context, but you're probably meant to assign to onLoad rather than call it:
props.onLoad = () => this.mapLoaded = true;
Typically, a callback is called by the object you're passing the interface to, so that it can call back to your code when something happens (in this case, presumably when a map is loaded).

Related

What is the difference of callback and module exports?

There is an article I've seen about the callbacks in javascript. https://codeburst.io/javascript-what-the-heck-is-a-callback-aba4da2deced I know that I can understand it by reading the article. However, I'm getting confused of the callback while studying the module export in node.js
Callback - A callback is a function that is to be executed after another function has finished executing
Callback in javascript
function doHomework(subject, callback) {
console.log(`Starting my ${subject} homework.`);
callback();
}
doHomework('math', function() {
console.log('Finished my homework');
});
Module export in node.js
//app.js
const logger = require('./logger');
logger.log(10, 10);
//logger.js
const multiply = require('./multiplication');
function log(valueOne, valueTwo) {
multiply('The result is ', valueOne, valueTwo);
}
module.exports.log = log;
//
function multiply(speech, valueOne, valueTwo) {
let result = valueOne * valueTwo;
return console.log(speech + result);
}
module.exports = multiply;
and ran the node app.js on my terminal.
The result that I got from running the node app.js is The result is 100 and that is correct.
But my question is
Does the approach that I did on the node app is consider as callback as well?
Callback - A callback is a function that is to be executed after another function has finished executing
That's not a correct definition of "callback." Unless that definition had major caveats on it you didn't quote, I wouldn't continue to use whatever resource you got that from.
A fairly broad definition of a callback is:
A callback is a function you pass to something else (as a function argument, property value, etc.) that the other thing will call when its defined criteria for calling the function are met.
Some might argue for a narrower definition:
A callback is a function you pass to another function for that other function to call back when its defined criteria for doing so are met.
Examples of callbacks:
DOM event handlers, although we usually call them "handlers" rather than callbacks. (Broad definition.)
The function you pass to Array.prototype.sort to compare array elements. (Both the broad and narrower definitions.)
The function you pass to new Promise to start the asynchronous operation the promise will observe (called the "promise executor function"). (Both the broad and narrower definitions.)
The function you pass to Array.prototype.map to transform elements. (Both the broad and narrower definitions.)
The function you pass to a promise's then, catch, or finally method.
The function you pass to fs.openFile that Node.js will call when the file has been opened (or the operation has failed). (Both the broad and narrower definitions.)
...and many others.
Notice that many of those (2, 3, and 4) are called before the function calling them has finished executing.
Does the approach that I did on the node app is consider as callback as well?
No. Although you use multiply in log, it's just a function you call from log, not a callback. This would be a callback:
function multiply(a, b, cb) {
cb(a * b);
}
function showResult(msg) {
console.log(msg);
}
multiply(7, 6, showResult);
showResult is used as a callback when calling multiply.
I don't entirely understand your question. However, from what I gather, module.exports does not make a function a callback function explicitly. The purpose of module.exports is to allow access to that function when requiring the relevant .js file...as seen in your example.
Your log() function is a not a callback as you are simply passing in parameters and then using those values to call the multiply function and output the result.
When you call the multiply function you are simply calling it like so:
multiply('some text', 10, 10)
For this to be a callback it would have to take a function as it's final parameter, i.e.:
multiply('some text', 10, 10, function(err, data) {
// ...
})
This also goes for the log function, and any for that matter.
So, unless the final parameter of a function is a function, it is a not a callback. module.exports purely allows access to that function or the functions you specify in the object, for example:
module.exports = {
functionOne: someFunctionName,
functionTwo,
functionThree
}
If the name of the function is the same name as what you are trying to export you do not need to specify a value to the key.

knockoutjs execute callback immediately after subscribe to observable

I have:
this.selectedItem.subscribe(model => {
this.currentEditor(this.editors.find(e => e.canRender(model)));
this.currentEditor().render(model);
});
where this.selectedItem already has a value.
Is it possible to run callback (but leaving it anonymous) immediately after subscribing?
You can call valueHasMutated on this.selectedItem, so
this.selectedItem.valueHasMutated()
However this not just runs your callback but also notify everybody else, e.g bindings, computeds etc.
So you are propably better to have a named function and call it when it is needed and use that also in the subscribe.
You can extract function into a variable and call it:
var changeHandler = (model) => {
this.currentEditor(this.editors.find(e => e.canRender(model)));
this.currentEditor().render(model);
};
this.selectedItem.subscribe(changeHandler);
changeHandler();

How do I create an observable from the onmessage callback?

I am fairly used to RX having used it in .NET and Java, I am expecting to be able to do the following:
Rx.Observable.fromCallback(websocket.onmessage)
.map(...)
.subscribe(...);
however, the console has the following:
Uncaught TypeError: Rx.Observable.fromCallback(websocket.onmessage).map is not a function
which would appear to indicate that the fromCallback is not returning an Observable.
What am I doing wrong here? Have I misunderstood what fromCallback is doing and I need to use a Subject? Can I not wrap some arbitrary handler in an observable?
You are actually looking for fromEvent or fromEventPattern:
Rx.Observable.fromEvent(websocket, 'message').map(/*...*/).subscribe();
Rx.Observable.fromEventPattern(
function add(h) { websocket.addEventListener(h); },
function remove(h) { websocket.removeEventListener(h); })
.map(/*...*/)
.subscribe();
The first one will attempt to use some of the standard ways of subscribing to an event emitter, which WebSocket is. However, if that fails you can use fromEventPattern instead to specify how handlers are added or removed from your object.
One additional note, JavaScript does not pass along an implicit reference to the instance of the object you are using as C# and Java do, so your code fromCallback(websocket.onmessage) is not passing along websocket, it is passing along the reference to the method from the function prototype. this will be determined at execution time.
Rx.Observable.fromCallback is for functions whose last argument is a callback function which is a standard pattern for asynchronous JavaScript code. Further, the fromCallback method does not return an Observable it returns a function that when called returns an Observable i.e.
function methodWithCallback(arg0, arg1, cb) {
setTimeout(function() {
cb(arg0 + arg1);
}, 2000);
}
var newMethod = Rx.Observable.fromCallback(methodWithCallback);
//[After 2 seconds] 3
newMethod(1, 2).subscribe(console.log.bind(console));

When I use a method as a callback, it seems to lose access to `this`. Why?

I'm using express in Node to create a simple web app. The code looks like this:
var get_stuff = function (callback) {
another.getter(args, function (err, data) {
if (err) throw err;
data = do_stuff_to(data);
callback(data);
});
};
app.get('/endpoint', function (req, res) {
get_stuff(res.send);
});
When I run this, though, I get this error: TypeError: Cannot read property 'method' of undefined at res.send. The express code that's breaking starts like this:
res.send = function (body) {
var req = this.req;
var head = 'HEAD' == req.method;
It seems to me that the way I've constructed the callbacks is losing this in the send method. But I'm not sure how to fix it. Any tips? Thanks!
Call .bind:
get_stuff(res.send.bind(res));
and have a look at the MDN documentation about this to get an idea how it works. The value of this is determined by how the function is called. Calling it "normally" (what probably happens with the callback), like
func();
will set this to the global object. Only if a function is called as an object method (or if this is explicitly set with .bind, .apply or .call are used), this refers to the object:
obj.fun(); // `this` refers to `obj` inside the function
.bind allows you to specify the this value without calling the function. It simply returns a new function, similar to
function bind(func, this_obj) {
return function() {
func.apply(this_obj, arguments);
};
}
In JavaScript, the value of this is generally determined by the call site, and unlike in Python, accessing a method via the . operator does not bind its left-hand side to this when the method is later called.
To perform the binding, you can call .bind like in the older answer, or you may perform the binding by hand, wrapping the method call in another callback:
get_stuff(function () {
return res.send.apply(res, arguments);
});
As of ECMAScript 2018, it’s also possible to use fat-arrow function syntax and rest parameters to make the above much more compact:
get_stuff((...args) => res.send(...args));

What to do when passing a parameter into a callback function conflicts with the callback's caller

I am using an ID3 reader script to retrieve data from audio files. The basic usage is:
ID3.loadTags(file,function()
{
var attr = ID3.getAllTags(file).attribute;
});
where the anonymous function is a callback function. This is just to provide context, however, I'm not at all sure the problem I'm having is specific to that particular script.
Typically, inside the callback function, you can extract the info you need and then use the DOM to set the innerHTML attribute of whatever element to equal the info you extracted.
Sometimes you're extracting a bunch of info and connecting all together in a long string though, and I'm trying to compartmentalize it a bit more so that my calling function will be a little cleaner. What I want to do is this:
function callingFunction()
{
var file = "whatever.mp3";
var info = getInfo(file);
}
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
}(info));
return info;
}
An object with an attribute is created because it's one of the only ways to simulate passing by reference in JS - pass the object into the callback function, assign the appropriate data to the attribute in the object, and then at the end of calledFunction, return the object to callingFunction.
It doesn't work though. Now, in that code above, if I said passedVar.data = "teststring" instead of assigning it data from dataobj, that would work, so the passing of the object into the callback function is working correctly. But if the object is assigned data from the data object that the ID3 function returns, it doesn't work. It comes back undefined, and furthermore, Chrome's JS debugger says that the object the ID3 function returns is null. This is further confirmed when I do this:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
alert(ID3.getAllTags(file).(any attribute));
}(info));
return info;
}
and no alert box comes up at all. But if remove the parameter being passed into the callback function in the code above, and leave everything else the same, that alert box comes up like it's supposed to.
So, to sum up, when I pass a parameter into the callback function, for some reason, another function of the object that's calling the callback function ceases to work correctly.
Is it possible that passing a parameter into the callback function is somehow conflicting with, or overriding, whatever the ID3.loadTags function is passing into the callback function, and that's why the getAllTags function is failing? Because for some reason when a parameter is passed into the callback function, the getAllTags function is no longer getting all of the information it needs to run properly? That's the only explanation I can think of.
If that's the case, is there a way to work around it? And if that's not what's going on, what is going on?
I have figured one solution, but I feel like it's hacky. I basically create a third function that gets called from the callback function(which itself receives no parameters), that takes as a parameter the object that the getAllTags method returns, extracts data from that object, and assigns it to global variables that other functions can access. So, this:
var globalVar;
function calledFunction(file)
{
//var info = {data: 0};
ID3.loadTags(file, function()
{
thirdFunction(ID3.getAllTags(file));
});
//return info;
}
function thirdFunction(dataobj)
{
globalVar = dataobj.title+etc;
}
But I don't really like that solution, I feel like it goes against the spirit of compartmentalization that I was after in the first place with this.
I'd appreciate any help.
The reason this doesn't work:
function calledFunction(file)
{
var info = {data: 0};
ID3.loadTags(file, function(passedVar)
{
var dataobj = ID3.getAllTags(file);
passedVar.data = dataobj.title+etc+dataobj.album+....(it can get long);
} (info));
// ^^^^^^ --- calls the function immediately
return info;
}
...is that you're calling your anonymous function and passing the result of that call (undefined) into ID3.loadTags. You're not passing a function into it anymore at all.
But the fundamental problem is that you're trying to use the data object before loadTags calls its callback and puts the data on the object.
I suggest that, since its output depends on an asynchronous operation, rather than relying on function return values, you change calledFunction to take a callback function. Here's what it should look like:
function callingFunction() {
getInfo('whatever.mp3', function(info) { // pass a callback function
// info.data is here now
});
}
function getInfo(file, cb) { // accept a callback function as the 2nd param
ID3.loadTags(file, function() {
var tags = ID3.getAllTags(file);
// once your async operation is done, call cb and pass back the return value
cb({
data: tags.title+etc+tags.album+....(it can get long);
});
});
}
This approach avoids problems you were trying to solve by using an object you could pass by reference, and it ensures you only move on once your asynchronous operation (ID3.getAllTags) is complete.

Categories

Resources