反应式扩展 (RxJS) 简介
介绍
RxJs,即 Javascript 的 Reactive Extensions,是 Javascript 的 Observables 实现。RxJS 有多种实现,涵盖多种语言,例如 RxJAVA、Rx.NET 等。Angular
在内部使用 RxJS 来实现部分功能。RxJS 库具有创建和使用 Observables 的方法。我们可以使用这些实用方法将与异步操作相关的现有代码转换为可观察对象,迭代某些流中的值等。
流
流只不过是一系列随时间变化的值。例如,如果有一个计数器每秒增加 2,那么该计数器可能具有如下流:
0
甚至可以有一个流对应于用户在 Web 应用表单中输入一些值。在这种情况下,流可以表示每个按键,如下所示:
"A"
该流甚至可以对应我们表单的 JSON 表示,如下所示;
{ "name": "A" }
响应式编程
通过反应式编程,我们通过定义各种流以及在这些流上执行的操作来创建整个应用程序。
虽然从纸面上理解这也许很容易,但我们实际上该如何训练自己进行反应式思考和编程呢?
让我们尝试将基本的命令式函数转换为反应式函数。
adder (a, b) {
return a + b;
}
c = adder (10, 20);
c = adder (10, 40);
如上所示,我们得到了一些状态变量 a、b 和 c。此外,还有一个名为 adder 的函数。
为了将 a 和 b 相加,并更新变量 c 的状态为它们的加法,我们称之为函数加法器。
一段时间后,变量 b 的值更新为 50。
现在,这里有几点需要注意;
- 我们需要找到一种方法来知道变量 b 已经更新,这本身就很难弄清楚。
- 我们还需要知道,由于变量 b 已经改变,我们现在必须重新计算变量 c。
现在,上面的程序可能过于简单。但是当我们将其扩展到 Web 应用程序时,我们意识到输入会通过用户点击某处、鼠标事件、网络事件等不断更新。
我们的大部分应用程序逻辑都是基于确定对于输入发生的每个变化需要调用哪些方法。
当进行反应式编程时,我们实际上并不是从变量的角度来思考,而是从流的角度来思考,以及各种流如何连接在一起。
因此,在我们的示例中,我们将 a、b 和 c 转换为流。
因此,a 不再是任何时刻的单个值,而是随时间变化的值流。
加法器可以被认为是将两个流 a 和 b 的输出连接到 c 的输入的操作。
如果现在将一些数字推送到流 a 和 b,则会自动调用加法器。它计算总数并将结果推送到流 c。
例如,如果 c 也连接到来自其他操作(例如乘法器)的其他流,那么在这种情况下,甚至乘法器操作也会自动被调用。
因此,我们可以看到,在响应式编程中,我们不会调用函数。相反,我们只是定义我们的应用程序如何连接在一起,然后开始将值推送到不同的流。然后,各种操作将自动为我们的应用程序处理一切。
因此,如果变量 b 的值发生变化,我们需要做的就是将这个新值推送到流 b,加法器操作将为我们处理任务。
可观察对象
虽然流更像是一个概念,我们使用运算符将流连接在一起,例如上面看到的加法器
可观察对象代表一种新的原始类型,它为创建流、订阅流、对更新的值做出反应以及合并不同的流以创建新的流提供了蓝图。
间隔
我们首先必须获取 RxJS Observable 的实例。为此,我们说:
let observable = Rx.Observable;
再次强调,可观察对象不是流。相反,它只是描述各种流以及它们如何通过运算符链接起来。
假设我们希望可观察对象创建一个流,并每两秒推送一个增加 1 的新数字。
使用 RxJS,我们可以使用操作符间隔,如下所示
let observable = Rx.Observable.interval(2000);
间隔以毫秒数作为第一个参数,这是将新数字推送到流之间的间隔。
此外,操作符会返回一个应用了前一个操作符的新可观察对象。这样我们就可以将操作符链接在一起,如下例所示;
let observable = Rx.Observable
.operator11();
.operator22();
.operator33();
.operator44();
.operator55();
订阅
我们上面创建的可观察对象不会自动开始将新数字推送到流。它会在获得订阅者时这样做,如下所示:
let observable = Rx.Observable
.interval(2000);
observable.subscribe(value => console.log("New value is : " + value));
因此,可观察对象现在将开始生成新数字。我们还添加了一个回调方法,以便在推送任何数字时做出反应。我们的输出现在看起来像:
New value is: 0
New value is: 1
New value is: 2
New value is: 3
New value is: 4
New value is: 5
New value is: 6
New value is: 7
New value is: 8
New value is: 9
承诺
Promise 类是当今大多数现代网络浏览器的一部分。因此,我们无需添加任何显式导入即可使用 Promises。
下面是一个使用 Promises 的简单示例:
/* create new promise object */
const promise = new Promise(resolve => {
setTimeout(() => {
resolve('Hey there, Welcome to the world of Promises!');
}, 1000)
});
/* Print the resolved value that gets emitted. */
promise.then(val => console.log("Resolved value is : " + val));
异步事件(例如上面的 setTimeout())将导致承诺被解决或被拒绝。
带有值流的 Observable 示例
上文中,我们使用了一个 Promise 来表示单个异步值。现在,我们将了解如何使用 Observable 来生成随时间发出的值流。
import { Observable } from "rxjs/Observable";
/* Create new Observable with the subscribe method */
const obs: Observable<string> = new Observable(observer => {
const interval = setInterval(() => {
observer.next('Hey there, Welcome to the world of Observable!');
}, 1000);
// clear out
return () => {
clearInterval(interval);
}
});
/* Adding Subscribe to receive notifications */
obs.subscribe(val => console.log(val));
对于 Observables,我们需要为 Observable 类添加显式导入,因为它们还不是 ECMAScript 标准。
然后我们创建一个新的 Observable,并添加一个订阅方法,每当观察者订阅该 Observable 时就会调用该方法。
我们可以利用 Observer 上的 next() 方法来发出值。
在上面的例子中,我们使用 setInterval() 方法来模拟随时间发出值的异步事件。在这里,每一秒之后,我们都会发出字符串“嘿,欢迎来到 Observable 的世界!”。
然后,我们有一个方法来清除间隔,每当所有观察者取消订阅可观察对象时就会调用该方法。
我们还调用 subscribe() 函数,该函数每次在 next() 向所有观察者发出值时都会被调用。
主题
Subject 是一种特殊的可观察对象,既是可观察对象又是观察者。Subject 类扩展了 Observable。因此,它继承了可观察对象的相同属性和方法。如果这听起来有点令人困惑,让我们看一个例子;
import { Subject } from "rxjs/Subject";
/* create a new instance of Subject */
const subject = new Subject<number>();
/* Subscribe to the above subject */
subject.subscribe(
next => console.log('value before subject 1:', next),
error => console.warn(error),
() => console.log('complete before subject 1')
);
subject.subscribe(
next => console.log('value before subject 2:', next),
error => console.warn(error),
() => console.log('complete before subject 2')
);
/* Start emitting some values */
subject.next(11);
subject.next(22);
subject.next(33);
subject.next(44);
/* Subscribe to our subject */
subject.subscribe(
next => console.log('after: ', next),
error => console.warn(error),
() => console.log('complete after')
);
/* Subscription would now start receiving notifications */
subject.next(55);
subject.complete();
这里我们再次需要显式导入 Subject 类,然后创建一个新实例。使用 TypeScript,我们将主题的泛型指定为数字类型。因此,我们可以预期可观察对象发出的值是数字类型。然后,我们创建两个订阅,并添加一个带有三个通知的回调方法,next、catch 和 complete,顺序与 subscribe() 函数的参数相同。我们将通知结果输出到控制台。我们还使用 next() 发出四个不同的值:11、22、33 和 44。我们还在发出前四个值后添加第三个订阅。然后,我们使用 next() 发出第五个值:55。然后我们 complete() 可观察流。因此,通知所有观察者该流将不再发出任何值。
我们的输出现在如下所示:
value before subject 1: 11
value before subject 2: 11
value before subject 1: 22
value before subject 2: 22
value before subject 1: 33
value before subject 2: 33
value before subject 1: 44
value before subject 2: 44
value before subject 1: 55
value before subject 2: 55
after: 55
complete before subject 1
complete before subject 2
complete after
订阅可观察对象的顺序很重要,因为第三个订阅会收到第四个值 (44)。此外,由于 Subject 是可观察对象,我们可以调用 next() 方法向我们的观察者发出任何其他值。
异步主题
AsyncSubject 扩展了 Subject,并继承了 Subject 的所有方法和属性。我们来看一下:
import { AsyncSubject } from "rxjs/AsyncSubject";
/* create instance of AsyncSubject */
const sub = new AsyncSubject<number>();
console.log("Welcome to the world of AsyncSubject");
/* Subscribe to our subject */
sub.subscribe(
next => <span class="hljs-variable la
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~