Why is this destructuring not working? - javascript

I am creating an observable like so:
return new Observable(sub => {
const {next, complete, error} = sub;
this.AuthHttp.get(`http://192.168.1.100:3000/api/users/${id}`)
.subscribe(res => {
let user = res.json();
next(user);
complete();
}, e => {
error(e.json());
});
})
Yet it nothing is happening in my front end when next() is expected to be called. If I make a minor change to the code so that sub.next() is called instead, everything works as expected. This indicates the underlying code is not flawed, just the way I am making a reference to next.
I have seen this form of destructuring used with the Observer class before (in an example online), so what am I doing wrong here?

Because the next, error and complete methods are object methods that must be called on an object instance.
When you use destructuring to obtain the functions and later call those functions, the calls are without context.
You cannot do what you've attempted for the same reason that this will not work:
const { toString } = new Date();
console.log(toString());
For more information, see this issue.

Related

Call an object method once the chained methods are resolved

I would like to create a custom api service that will work similarly to the supabase-js library. Example:
const { data, error } = await supabase
.from('cities')
.select('name, countries(*)')
.eq('countries.name', 'Estonia')
I cannot figure out how it is possible that the last method doesn't have to be something like .get() (which would perform the fetch request)
Ideally, I would like the fetch to be called automatically after the chained methods are resolved - sth. like this:
const { data, pending, error } = await useProductsService()
.whereIn('id', [123, 12, 521, 521])
.whereLowerThan('price', 300)
note: I used supabase just as an example, I want to use this with a completely different API
I know how to do it with sth like a .get() method at the end, however, I'd like to do it without it, if possible.
It's not a big deal to have to call one more method but I see it as an opportunity to learn something new as well.
Is it possible to do something like this? Or did I understand something wrong?
As you correctly point out, it is necessary to have a function call at the very end, to let the chain know that you have finished building it and that it's time to execute the DB query.
However, when you do the destructuring and extract the data property, if there is a getter function for data, then that will act as the function which is called that signals that the building of the query is complete.
class Chain {
from(s) {
this.config = []; // 'from' is always the first in the chain, so reset the config
this.config.push({from: s}); return this;
}
select(s) { this.config.push({select: s}); return this; }
eq(s) { this.config.push({eq: s}); return this; }
get data() {
console.log(this.config);
// do the query here
return 'this is the result';
}
}
const api = new Chain()
const { data } = api
.from('cities')
.select('name, countries(*)')
.eq('countries.name', 'Estonia')
console.log(data)

Dynamically build a function

Is it possible to dynamically build a function based off another functions parameters?
For example:
Base Function:
const someFunction = (functionName, functionParams) => {
// Function Built here
};
someFunction("colorFunction", ["red", true]);
I'd like it to build something similar to this: I'd need to deconstruct the Array into individual params, but I'm not sure how simple that is? And I have no idea how I'd use the first String to call the function name?
functionName(...functionParams);
Which in my head would sort of work like this:
const colorFunction = (color, bool) => {
console.log("Colour: " + color);
console.log("Bool: " + bool);
};
Bit confused by this - I feel like I'm not a million miles away, but I'm not certain! Any help would be great, thanks!!
Edit - Why?
I have a react component with a click event that fires off a redux action. Ideally this action would fire some stuff over to my reducer, and asynchronously call this "dynamic" function. I can do this with a load of if/elses, but I don't think that's a very clean way of achieving this, if building a function this way is possible.
First you need to determine where the functions you want to call dynamically are stored. If they are global functions then you can call them using window:
const someFunction = (functionName, functionParams) => {
window[functionName]();
};
If they are methods of an object, then you can do something similar using the object:
const someFunction = (functionName, functionParams) => {
myObject[functionName]();
};
As for how to pass the arguments, you have a couple options here. If you are running a recent version of JS, or using polyfills, then you can indeed use the spread operator:
const someFunction = (functionName, functionParams) => {
window[functionName](...functionParams);
};
Otherwise you can always rely on the apply method:
const someFunction = (functionName, functionParams) => {
window[functionName].apply(null, functionParams);
};
The first argument in the apply method is the context you wish to pass to your function, in your case it doesn't seem necessary, hence the null value.
Edit: corrected bind with apply as mentionned by Bergi
What you are looking for is Function#bind in order to make a thunk - basically a function that takes no parameters and it's used for delayed computation.
Using .bind you can do a partial application on a function. In your case, you would just apply all arguments and only leave the execution step at the end:
//your function
const colorFunction = (color, bool) => {
console.log("Colour: " + color);
console.log("Bool: " + bool);
};
//partially apply all argument to it
const thunk = colorFunction.bind(null, "red", true);
//this can now be passed around and executed at a later point
setTimeout(thunk, 3000);
console.log("wait 3 seconds");
Since functions are first class members in JavaScript, you can pass any function this way. If you really need a function that turns others into thunks then you can very easily do that:
const toThunk = (func, args) => func.bind(null, ...args);
Which is your someFunction but with just the name and parameters re-named for a bit of clarity.

Test code inside an rxjs subscription when using jasmine marbles

I want to be able to test the code that runs inside an observable subscription:
function foo(someStream$: Observable<number>) {
someStream$.pipe(
map((x) => x + 3),
).subscribe((result) => {
SomeService.someFunc(result)
})
}
For example, I want to make sure someFunc() gets called with result. I would think that I should be able to do something like
const someStream$ = cold('-a--', { a: 5 })
const someFuncSpy = spyOn(SomeService, 'someFunc')
foo(someStream$)
expect(someFuncSpy).toHaveBeenCalledWith(8)
Problem is that I will be told that someFunc was never called. I do not want to separate the stream and the subscribe into separate functions.
you could use Rx.Observable.of(5) instead of cold() to pass a number through the observable

Need clarification for map and observable syntax in Angular

There is a certain syntax that is baffling me and i see it with the map function and also with the observable in typescript/Angular (Angular 5). I have two methods:
This one is in a component:
logout() {
this.authService.logout().subscribe(
result => {
this.router.navigate(['/login']);
}
);
}
And this is in the related service:
logout(): Observable<any> {
return this.http.post('/api/auth/logout', { }).map(
response => {
this._token = null;
//more unrelated code...
return true
}
);
}
The part that is confusing me in both of these cases is this:
thing => {
//code
}
What is this? The code above works. but I see that have both 'result' and 'response' for thing. Can 'thing' be anything at all or is it defined somewhere?
Also, I looked up the map function in javascript at w3schools (because I've never had a use for it) and it shows in the example that the first parameter is supposed to be a function which gets applied to each element of the array that it is associated with but "thing => {}" is not a function so this is super confusing.
Note, that I have worded my question in such a way as to get to the underlying misunderstanding rather than focusing on my specific problem, however solving my specific problem may help illustrate my misunderstanding.
The problem with the code above is that while it works it does not know what to do when the api endpoint returns a 500 error. I am trying to determine how to catch the error so that I can do something with that on the front end.
Thing can be whatever you want to name it. Result, data, response, etc. Doesn't matter. What you're basically doing is creating a variable for the result emitted from your subscription. The subscription takes in a function() and inside that function, you pass the variable name that you want to be used for the success result. And really, here, using result is meaningless, since nothing is ever done with it. If you aren't going to do anything with the response, its better to just say:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
});
}
To catch errors, you only need to pass a comma after the last curly, like so:
logout() {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
}, err => {
// Do something with error here
});
}
As for map, here is an example
var array1 = [1, 4, 9, 16];
const map1 = array1.map(x => x * 2);
It basically takes every variable in the array and performs that map method, meaning it takes each value and does whatever the function says to do, in this case, multiply it by 2. Think of it as a sort of transformation. In that example, it's basically being used to manipulate the response before sending it back to the subscription.

javascript - decorate a class instance without explicitly forwarding all functions

Let me start by saying this is more of a curiosity question because, as you will see, I was able to achieve the desired functionality.
However, given that javascript is a super flexible language, I would like to see what other devs might think about this problem:
I have an instance of a class which is returned from a vendor function:
const connection = vendorDatabaseLib.createConnection();
Now, I would like to create a decorator which will add functionality to the connection class, for example, reconnection logic.
Lets call it PersistentConnection. Apart from my added custom functions I would like an instance of PersistentConnection to forward all function calls to the original Connection instance. And in some functions override the behaviour.
I could of course implement all Connection's functions explicitly and forward them to the inner object but there might be lots of these functions, so I quickly discarded this idea.
So here are my ideas of how to achieve this:
Monkey patching 🐒, Instead of a decorator I can create a PersistentConnection class which inherits from the vendor Connection and then patch the vendor vendorDatabaseLib.createConnection function to return PersistentConnection with all my desired added functionality. Tempting, but bad.
Create a decorator which iterates over the Connection functions and creates forwards dynamically, something like:
class PersistentConnection{
constructor(connection){
this._connection = connection;
// Iterate through all functions
for (prop in this._connection){
if(typeof(this._connection[prop]) === 'function'){
// Create functions dynamically for each forward
this[prop] = (...args) => {
this._connection[prop](...args);
}
}
}
}
// This is the added logic
reconnect(){
// Custom logic
}
}
Set the Connection instance to be a the prototype of PersistentConnection's instance:
function persistenChannel(channel){
const persistentChannel = {};
Object.setPrototypeOf(persistentChannel, channel);
persistentChannel.reconnect = () => {
// custom logic
}
}
This is the most "automatic" way I could think of.. But it just down right ugly, and the custom functions need to be declared each time an instance is created.
I still feel like I'm missing something, something like Ruby's magical method_missing (or pythons __getattr__) function which is called just before a method is missing exception is thrown and lets you define "safety net" logic (like delegating all calls to the inner _connection object.
Is there a better way to achieve this functionality?
Thanks a lot [=
Lets start from what we have. In any case, most of the functionaliy will be performed by vendor object. We do not know details realization so we can't rely that this object has no state. This mean, that in any case we need new connection object for the new persistentConnection. This can be achieved with proxy object
Lets try to do this:
function Connection() {
this.connect = () => console.log('connected by Connection class');
this.disconnect = () => console.log('disconnected by Connection class');
}
function persistantConnectionFactory() {
function PersistentConnection() {
this.checkConnection = () => console.log('no connection');
}
const instance = new PersistentConnection();
const proxy = new Proxy(instance, {
get: function (target, name) {
if (!(name in target)) {
console.log('adding new prototype')
Object.setPrototypeOf(instance, new Connection())
}
return target[name];
}
});
return proxy;
}
var c = persistantConnectionFactory();
c.checkConnection();
c.connect();
Does this solution good? I think - not. Without very good reasons this adds complexity without any value. Prototype should be enough.

Categories

Resources