繁体   English   中英

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

[英]Why does RxJS subscribe allow omitting the arrow function and the following method argument?

最近我需要使用 RxJS。 我试图设计一个错误处理流程,但我发现了一些奇怪的语法传递方法 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

重现步骤:

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

如果没有箭头 function,它仍然会将错误传递给 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;
    })
  );
}

我试图用谷歌搜索一些关键字,js,rxjs,angular,省略箭头 function,参数丢失,...但我找不到这里使用的技术。

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

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

线

地图(this.extractTreeData)

相当于

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

如何将额外参数传递给 RxJS map 运算符

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

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

因此,当您使用箭头 function 时,您定义了一个就地回调 function。

sourceObservable.subscribe({
  next: (value) => { },
  error: (error) => { },
  complete: () => { }
});

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

onNext(value) {
}

onError(error) {
}

onComplete() {
}

sourceObservable.subscribe({
  next: this.onNext,
  error: this.onError,
  complete: this.onComplete
});

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

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

sourceObservable.subscribe({
  next: this.onNext.bind(this),
  error: this.onError.bind(this),
  complete: this.onComplete.bind(this)
});

因此,如果您只希望有错误回调,例如,您可以显式 state 并忽略其他。

sourceObservable.subscribe({ error: console.warn });

现在关于您的问题“为什么在 function 调用中没有括号”这里这里都进行了讨论。 arguments 期望引用 function 并且 function 名称表示它们的引用。

console.log是 function

function可以用括号中的arguments调用

console.log("123")表示使用参数"123"调用 function console.log

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

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

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

function example(callback) {
callback();
}

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

function example(callback) {
console.log();
}

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

function example(callback) {
(tree => console.log(tree))();
}

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

function subscribe(nextCb, errorCb, completeCb) {
// ... got next data
nextCb(data);
//... got error
errorCb(error);
// completed observe
completeCb();
} 

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

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

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

在 JS 函数中,首先是 class 对象 当您有代码console.warn没有括号时,您有对此 object 的引用,但您没有调用 object,这需要大括号console.warn() 例如,您可以这样做:

 let x = console.warn; console.log('not happened yet'); x('test');

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

Subscribe(() => {}, () => {});

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

另一部分是你抛出错误throw "is not 7"; . 因此,订阅错误调用的签名是:

subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription;

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

console.warn(obj1 [, obj2, ..., objN]);

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

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

.subscribe(
    x => {
    },
    x => {console.warn(x);} 
);

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

这是由于 Observable 能够发出的三种可能类型的值,

  1. 下一个
  2. 错误
  3. 完全的

These logic is translated in the arguments of the subscription function, so the first function callback will trigger the values emitted via next, the second callback the values emitted with error and the third function with the complete value.

在您的情况下,console.warn 将作为 function 传递给第二个 function,每次发出错误时都会调用它。

对于第二个问题,您可以参考箭头 function 文档, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

您的订阅需要 2 个 arguments。 第一个参数是 function ,它将使用“下一个”值调用,第二个参数再次是 function,如果发生错误,将调用它。 因此,由于“console.warn”是 function,您可以将其用作第二个参数。

(error) => console.warn(error)console.warn(error)一样

但是要小心,因为console.warn不依赖于上下文“this”,你不会给出任何问题。 但是,如果您想使用使用上下文“this”的 function,则需要使用箭头 function。

有关 JS 范围的更多信息: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM