Recently I needed to use RxJS. I tried to design an error handling flow, but I discovered some weird syntax passing method arguments:
.subscribe(
x => {
},
console.warn // <- Why does this compile, and warn 'is not 7' in debug console?
);
https://stackblitz.com/edit/rxjs-6-5-error-handle-no-arrow-issue
,console.warn
, not like ,error => { console.warn(error); }
Without an arrow function, it still passes errors to console.warn. Why?
Code:
import { throwError, concat, of } from "rxjs";
import { map } from "rxjs/operators";
const result = concat(of(7), of(8));
getData(result).subscribe(
x => {
console.log("typeof(x)", typeof(x));
if (typeof(x) === 'string') {
console.log("x Error", x);
return;
}
console.log("no error", x);
},
console.warn // <- Why does this compile, and warn 'is not 7' in debug console?
);
// pretend service method
function getData(result) {
return result.pipe(
map(data => {
if (data !== 7) {
throw "is not 7";
}
return data;
})
);
}
I tried to google some keywords, js, rxjs, angular, omit arrow function, argument missing,... but I cannot locate what tech is used here.
Could anyone provide links to where this mechanism is explained?
The following two questions are related but do not explain the behavior, just say "equivalent":
The line
map(this.extractTreeData)
is the equivalent of
map(tree => this.extractTreeData(tree))
How to pass extra parameters to RxJS map operator
Why is argument missing in chained Map operator
First you need to understand what you're actually passing to the .subscribe
function. Essentially it accepts three optional arguments next
, error
and complete
. Each of it is a callback to be executed when the corresponding notification is emitted by the source observable.
So when you're using an arrow function, you define an in-place callback function.
sourceObservable.subscribe({
next: (value) => { },
error: (error) => { },
complete: () => { }
});
Instead you could define the functions separately and use it as callbacks.
onNext(value) {
}
onError(error) {
}
onComplete() {
}
sourceObservable.subscribe({
next: this.onNext,
error: this.onError,
complete: this.onComplete
});
Now this is what you're seeing. But instead of a user-defined function, you're passing the built-in console.warn()
function. And in-turn the values from the notifications will be passed as arguments to the callback functions. So the value from your error is not 7
is sent as argument to console.warn()
which then does it's job (i.e. prints to the console).
However there's a catch. If you wish to refer to any of the class member variables using the this
keyword in the callback, it'll throw an error saying the variable is undefined. That's because this
refers to the scope of the function in the callback and not the class. One way to overcome this is to use an arrow function (we've seen that already). Or use bind()
function to bind the meaning of this
keyword to the class.
sourceObservable.subscribe({
next: this.onNext.bind(this),
error: this.onError.bind(this),
complete: this.onComplete.bind(this)
});
So if you wish to only have the error callback for example, you could explicitly state it and ignore the others.
sourceObservable.subscribe({ error: console.warn });
Now as to your question "why no parentheses in the function call", it was discussed here and here. The arguments expect a reference to a function and the function names denotes their reference.
console.log
is a function
function can be called with arguments in a bracket
console.log("123")
means call function console.log
with argument "123"
tree => console.log(tree)
is also a function
it can also be called with arguments in a bracket, eg.
(tree => console.log(tree))(tree)
so a function with callback as its argument can call its callback with arguments in a bracket
function example(callback) {
callback();
}
so if we pass console.log
to it, example(console.log)
, it basically runs as
function example(callback) {
console.log();
}
if we pass tree => console.log(tree)
to it, example(tree => console.log(tree))
, it basically runs as
function example(callback) {
(tree => console.log(tree))();
}
if you understood above code. it's easy to understand subscribe now.
function subscribe(nextCb, errorCb, completeCb) {
// ... got next data
nextCb(data);
//... got error
errorCb(error);
// completed observe
completeCb();
}
so your error callback console.log
basically get called as console.log(error)
;
error=> console.log(error)
basically get called as (error=> console.log(error))(error)
;
which in this case results are same.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With