为什么 RxJS 订阅允许省略箭头函数和以下方法参数?

Pau*_*000 5 javascript operator-keyword rxjs angular

最近我需要使用 RxJS。我试图设计一个错误处理流程,但我发现了一些奇怪的语法传递方法参数:

.subscribe(
    x => {
    },
    console.warn // <- Why does this compile, and warn 'is not 7' in debug console?
);
Run Code Online (Sandbox Code Playgroud)

链接到最小复制:

https://stackblitz.com/edit/rxjs-6-5-error-handle-no-arrow-issue

重现步骤:

  1. 使用 RxJS 6.5
  2. 创建一个函数返回 observable
  3. 订阅可观察的
  4. 将参数传递给订阅
  5. 只是使用,console.warn,不喜欢,error => { console.warn(error); }

如果没有箭头函数,它仍然会将错误传递给 console.warn。为什么?

代码:

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;
    })
  );
}
Run Code Online (Sandbox Code Playgroud)

我试图用谷歌搜索一些关键字、js、rxjs、angular、省略箭头函数、缺少参数……但我找不到这里使用的技术。

谁能提供解释此机制的链接?

以下两个问题是相关的,但不解释行为,只说“等效”:

线

地图(this.extractTreeData)

相当于

地图(树 => this.extractTreeData(树))

如何将额外的参数传递给 RxJS 地图操作符

为什么链式 Map 运算符中缺少参数

Mic*_*l D 8

首先,您需要了解您实际传递给.subscribe函数的内容。本质上它接受三个可选参数next,errorcomplete。每一个都是在源 observable 发出相应通知时执行的回调。

所以当你使用箭头函数时,你定义了一个就地回调函数。

sourceObservable.subscribe({
  next: (value) => { },
  error: (error) => { },
  complete: () => { }
});
Run Code Online (Sandbox Code Playgroud)

相反,您可以单独定义函数并将其用作回调。

onNext(value) {
}

onError(error) {
}

onComplete() {
}

sourceObservable.subscribe({
  next: this.onNext,
  error: this.onError,
  complete: this.onComplete
});
Run Code Online (Sandbox Code Playgroud)

现在这就是你所看到的。但是,您传递的是内置函数,而不是用户定义的console.warn()函数。反过来,通知中的值将作为参数传递给回调函数。因此,您的错误中的值is not 7作为参数发送console.warn(),然后执行它的工作(即打印到控制台)。

但是有一个问题。如果您希望this在回调中使用关键字来引用任何类成员变量,它会抛出一个错误,指出该变量未定义。那是因为this在回调中指的是函数的作用域,而不是类。克服这个问题的一种方法是使用箭头函数(我们已经看到了)。或者使用bind()函数将this关键字的含义绑定到类。

sourceObservable.subscribe({
  next: this.onNext.bind(this),
  error: this.onError.bind(this),
  complete: this.onComplete.bind(this)
});
Run Code Online (Sandbox Code Playgroud)

因此,例如,如果您只想拥有错误回调,则可以明确说明它并忽略其他回调。

sourceObservable.subscribe({ error: console.warn });
Run Code Online (Sandbox Code Playgroud)

现在至于你的问题“为什么函数调用中没有括号”这里这里讨论过。参数期望对函数的引用,函数名称表示它们的引用。


ars*_*012 5

console.log 是一个函数

可以使用括号中的参数调用函数

console.log("123")表示console.log带参数调用函数"123"

tree => console.log(tree) 也是一个函数

它也可以用括号中的参数调用,例如。 (tree => console.log(tree))(tree)

所以一个以回调为参数的函数可以用括号中的参数调用它的回调

function example(callback) {
callback();
}
Run Code Online (Sandbox Code Playgroud)

所以如果我们传递console.log给它example(console.log),它基本上运行为

function example(callback) {
console.log();
}
Run Code Online (Sandbox Code Playgroud)

如果我们传递tree => console.log(tree)给它example(tree => console.log(tree)),它基本上运行为

function example(callback) {
(tree => console.log(tree))();
}
Run Code Online (Sandbox Code Playgroud)

如果你理解上面的代码。现在订阅很容易理解。

function subscribe(nextCb, errorCb, completeCb) {
// ... got next data
nextCb(data);
//... got error
errorCb(error);
// completed observe
completeCb();
} 
Run Code Online (Sandbox Code Playgroud)

所以你的错误回调console.log基本上被称为console.log(error);

error=> console.log(error)基本上被称为(error=> console.log(error))(error);

在这种情况下,结果是相同的。


Lia*_*iam 2

在 JS 中,函数是第一类对象。当您的代码console.warn没有括号时,您拥有对此对象的引用,但您没有调用该对象,则需要大括号console.warn()。例如你可以这样做:

let x = console.warn;
console.log('not happened yet');
x('test');
Run Code Online (Sandbox Code Playgroud)

因此,您的代码很简单,以与传递任何其他函数完全相同的方式将console.warn函数传递给失败的参数,例如Subscribe

Subscribe(() => {}, () => {});
Run Code Online (Sandbox Code Playgroud)

[为什么] 显示警告“不是 7”

另一部分是你抛出了一个错误throw "is not 7";。Subscribe 的错误调用的签名如下:

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;
Run Code Online (Sandbox Code Playgroud)

所以 的参数的error类型是any。因此, throw 将 an 传递Error给错误函数处理程序。这被设置为console.warn具有以下签名:

console.warn(obj1 [, obj2, ..., objN]);
Run Code Online (Sandbox Code Playgroud)

console.warn本质上将其传递的任何参数转换为字符串,JS 不是强类型的,这本质上取决于类型 coercion,并记录它。的字符串throw "is not 7";is not 7. 所以它记录了is not 7.

总而言之,我想说这一切都有点神秘并且可能难以理解。这里在技术上没有任何问题,但我想说,执行以下操作更有意义:

.subscribe(
    x => {
    },
    x => {console.warn(x);} 
);
Run Code Online (Sandbox Code Playgroud)

基于“任何傻瓜都可以编写计算机可以理解的代码。优秀的程序员编写人类可以理解的代码”的原则。