博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
rxj热血江hsf湖私服_如何使用RxJ进行React性思考和动画化移动对象
阅读量:2525 次
发布时间:2019-05-11

本文共 28700 字,大约阅读时间需要 95 分钟。

rxj热血江hsf湖私服

These days, many software systems have to deal with asynchronous behaviors and time-related issues.

如今,许多软件系统必须处理异步行为和与时间有关的问题。

Continuous connectivity, distributed systems, microservices-based architectures, the cloud, non blocking platforms — the consequence of all these things is that we somehow have to deal with asynchronicity and time. Our software systems have to learn how to deal with streams of events, which are, by their nature, asynchronous.

连续连接,分布式系统,基于微服务的体系结构,云,无阻塞平台-所有这些事情的结果是,我们必须以某种方式处理异步性和时间。 我们的软件系统必须学习如何处理事件流,这些事件流本质上是异步的。

Reactive programming provides powerful tools, based on a functional programming style, that help us model systems that work in such a world. But these systems require us to think reactively when we design our solutions.

响应式编程基于功能性编程风格提供了功能强大的工具,可帮助我们对在这样的世界中工作的系统进行建模。 但是这些系统要求我们在设计解决方案时做出React。

Thinking reactively often represents a challenge, as does any change of perspective. At the same time, it may be easier than you expect. Just look at what happens in the real world and try to map it in a straightforward way.

与观点的任何改变一样,被动地思考通常代表着挑战。 同时,它可能比您预期的要容易。 只需看看现实世界中发生的事情,然后尝试以一种简单的方式将其映射即可。

In this article, I aim to show you how to apply reactive and functional thinking to solve a very well-known problem in a natural way: how to animate an object with controlled motion. The metaphor I’ll use is that of a vehicle which can accelerate and brake, following the commands issued by a remote controller.

在本文中,我旨在向您展示如何运用React性和功能性思维以一种自然的方式解决一个非常著名的问题:如何以受控的运动为对象设置动画。 我将使用的隐喻是按照遥控器发出的命令可以加速和制动的车辆的隐喻。

In the implementation we’ll be using RxJs, the JavaScript version of ReactiveX, and Typescript.

在实现中,我们将使用RxJ,ReactiveXJavaScript版本和Typescript。

The code for a full demo implementation can be found .

完整的演示实现代码可在找到。

If you like this, around these themes.

如果您愿意, 围绕这些主题 。

快速回顾动力学的简单基础 (A quick recap of the simple basics of dynamics)

If you want to change the velocity of an object, you need to apply a force to it which in turn impresses an acceleration to the same object. If you know the value of acceleration A of the object, you can calculate the variation of its velocity dV in a certain time interval dT with the formula

如果要更改对象的速度,则需要对其施加力,从而对同一个对象施加加速度。 如果知道对象的加速度A的值,则可以使用公式计算在特定时间间隔dT中其速度dV的变化

dV = A * dT

dV = A * dT

Similarly, if you know the velocity V, then you can calculate the variation in space dS in a time interval dT with the formula

同样,如果知道速度V,则可以使用以下公式计算时间间隔dT中空间dS的变化:

dS = V * dT

dS = V * dT

Conclusion: if you have an acceleration A impressed to an object whose initial velocity is V0, you can approximate the velocity of the object in the time interval dT with its average, like this:

结论:如果您对初始速度为V0的物体施加了加速度A ,则可以在时间间隔dT中以其平均值来近似物体的速度,如下所示:

averageVel = (V0 + V1) / 2 = (V0 + V0 + dV) / 2 = V0 + A/2 * dT

averageVel =(V0 + V1)/ 2 =(V0 + V0 + dV)/ 2 = V0 + A / 2 * dT

and then calculate the approximate variation of space dS in the same interval dT with the formula

然后用公式计算在相同间隔dT中空间dS的近似变化

dS = averageVel * dT = V0 * dT + A/2 * dT²

dS = averageVel * dT = V0 * dT + A / 2 *dT²

The shorter the time interval dT, the better the approximation.

时间间隔dT越短则近似值越好。

“用运动为物体动画”的含义 (What “animating an object with movement” means)

If we want to animate an object with a movement controlled by acceleration, (that is, if we want to simulate how an object would move if subject to forces), we have to introduce the dimension of time.

如果要通过加速度控制的运动为对象设置动画(即,如果要模拟对象在受力作用下的运动方式),则必须引入时间的维度。

We have to divide the time in intervals, dT, calculate the space travelled for every dT, and show the new position at every interval.

我们必须将时间划分为间隔dT,计算每个dT的行进空间,并显示每个间隔的新位置。

使用PULL方法-询问信息 (Using the PULL approach — ask for information)

We can use the above function, and pull from it the information we need (how much the object moved during the last time interval dT given a certain acceleration A and initial velocity V). We would take the result of the function and use it to calculate the new position, as long as we are able to somehow remember the previous position.

我们可以使用上面的功能, 然后从它我们需要的信息(对象期间给予一定的加速A 和初始速度V的最后时间间隔的dT多少移动)。 只要我们能够以某种方式记住前一个位置,我们就可以使用该函数的结果并将其用于计算新位置。

If we rely on a pull approach, it is the caller (the SW component) calling the function that does most of the work. It keeps and updates state, controls time, and manages the entire movement.

如果我们依靠拉方法,则调用方(SW组件)会调用函数来完成大部分工作。 它保持并更新状态,控制时间并管理整个运动。

React方式:PUSH(和命令)方式 (The reactive way: the PUSH (and command) approach)

If you think of a vehicle which is controlled remotely by someone, then you would probably imagine that:

如果您想到某人远程控制的车辆,那么您可能会想到:

  • the vehicle transmits at a regular frequency its position and velocity to the controller

    车辆以固定的频率将其位置和速度传输到控制器
  • the controller can change the acceleration of the vehicle (steering and braking are just changes in the accelerations along the space axis) to guide the vehicle’s movement

    控制器可以改变车辆的加速度(转向和制动只是沿空间轴的加速度的变化)来引导车辆的运动

Such an approach has the advantage to clearly separate responsibilities:

这种方法的优点是可以明确区分职责:

  1. the vehicle is responsible for transmitting its state at any moment to any interested party

    车辆有责任随时将状态传达给任何相关方
  2. the controller is responsible for listening to the data transmitted by the vehicle and for issuing the right commands

    控制器负责侦听车辆传输的数据并发布正确的命令

Reactive programming provides the tools to build a software solution to this problem mirroring exactly this model. This is probably what you would expect in the real world:

React式编程提供了工具,以针对此问题构建软件解决方案,以准确地反映此模型。 这可能是您在现实世界中所期望的:

  • a vehicle that transmits the details of its dynamics (for example, speed, position, direction) — the Observable

    传递其动力学细节(例如,速度,位置,方向)的车辆-Observable
  • a controller that listens to such transmissions and issues commands to accelerate, decelerate, steer, and brake — the Observer

    监听此类传输并发出命令以进行加速,减速,转向和制动的控制器—观察者

React式实施-RxJ (Reactive implementation — RxJs)

To develop the solution, we use Typescript as our programming language and the ReactiveX model via RxJs implementation. But the concepts can be easily transposed to many of the other languages supported by ReactiveX.

为了开发解决方案,我们使用Typescript作为我们的编程语言,并通过RxJs实现使用ReactiveX模型。 但是,这些概念可以轻松地转换为ReactiveX支持的许多其他语言。

MobileObject类-在空间中移动的对象的表示形式 (The MobileObject class — a representation of objects that move in space)

We are going to build our simulator using reactive techniques with a functional programming style. But we’ll still use good old object-oriented (OO) concepts to build a clear frame for our implementation. So let’s start with the MobileObject class:

我们将使用功能性编程风格的React技术来构建模拟器。 但是,我们仍将使用良好的旧的面向对象(OO)概念为我们的实现建立清晰的框架。 让我们从MobileObject类开始:

export class MobileObject {}

This class will represent the objects that transmit at regular intervals of time all relevant data about their dynamics, like speed, position, and acceleration. Within this class we will work reactively.

此类将表示以固定的时间间隔传输有关其动力学的所有相关数据(如速度,位置和加速度)的对象。 在本课程中,我们将进行被动式工作。

让我们介绍一下Observable先生,它是我们MobileObject的核心 (Let’s introduce Mr. Observable, the core of our MobileObject)

As we know, to be controlled remotely, a vehicle must continuously transmit to its controller data about itself, namely:

我们知道,要进行远程控制,车辆必须连续地向其控制器传输有关其自身的数据,即:

  • its current velocity

    当前速度
  • its current position

    当前位置
  • how much its position and velocity varied since the last interval of time

    自上次间隔以来,其位置和速度有多少变化

This is just a stream of data over time emitted by the vehicle. The ReactiveX Observable is a way to model streams of events carrying data over time. So we can use Observables to model the data transmitted by our vehicle.

这只是车辆发出的随时间推移的数据流 。 ReactiveX Observable是一种对随时间推移承载数据的事件流进行建模的方法。 因此,我们可以使用Observables对我们的车辆传输的数据进行建模。

我们的时钟:一系列时间间隔 (Our clock: a sequence of time intervals)

The first thing we need to create is a sequence of time intervals. Each event emitted in this sequence knows the time elapsed since its predecessor, as illustrated in the following diagram:

我们需要创建的第一件事是一系列时间间隔。 如下图所示,此序列中发出的每个事件都知道自其上一个事件以来所经过的时间:

With RxJs we can create such a clock with an Observable using the following function:

使用RxJ,我们可以使用以下函数创建带有Observable的时钟

private buildClock(frameApproximateLenght: number) {  let t0 = Date.now();  let t1: number;  return Observable.timer(0, frameApproximateLenght)    .do(() => t1 = Date.now())    .map(() => t1 - t0)    .tap(() => t0 = t1)    .share();}const clock = buildClock(xxx);

Let’s call this observable clock. Our clock emits approximatively every xxx milliseconds. Each event emitted by clock will carry the exact number of milliseconds elapsed since the previous emission.

我们将此称为可观察时钟 。 我们的时钟大约每xxx毫秒发出一次。 时钟发出的每个事件将携带自上一次发出以来经过的确切毫秒数。

We will see later, when talking about animation frames, why this method for creating an observable of time intervals is convenient. Later we will also cover why it is important to use the share operator while creating the clock.

稍后我们将在讨论动画帧时看到,为什么这种创建可观察时间间隔的方法很方便。 稍后我们还将介绍为什么在创建时钟时使用share运算符很重要。

计算时间间隔内速度和空间的变化 (Calculate the variation of speed and space in a time interval)

Let’s assume MobileObject is subject to an acceleration A. Now that we a clock, we can calculate the variation of speed dV using the formula dV = A * dT. Using this formula and the map operator of RxJs, we can create an Observable that emits the variation of speed over time:

假设MobileObject的加速度为A。 现在我们有了一个时钟 ,我们可以使用公式dV = A * dT计算速度dV的变化 使用此公式和RxJs的map运算符,我们可以创建一个Observable,它发出速度随时间的变化:

If we store in a variable velocity vel at time tX, we can calculate the approximate variation in space at the next time interval t(X+1) with the formula dS = vel * dT + A / 2 * dT². Again, using the map operator, we can obtain an Observable that emits the variation of space over time.

如果我们在时间tX处以可变速度vel存储,则可以使用公式dS = vel * dT + A / 2 *dT²计算下一个时间间隔t(X + 1)的空间近似变化。 同样,使用map运算符,我们可以获得一个Observable,它发出空间随时间的变化。

Using the same approach, we can build an observable that emits at every tick of the clock all the relevant information about the dynamics of MobileObject, starting just from its acceleration A. We call this observable dynamics.

使用相同的方法,我们可以构建一个可观察的对象,该对象在时钟的每一个滴答声中都发出有关MobileObject动态的所有相关信息,仅从其加速度A开始 。 我们称之为可观察的动态

But acceleration can change — so what?

但是加速度可以改变-那又如何呢?

This works if we know the acceleration A and if A is a constant.

如果我们知道加速度AA为常数,则此方法有效。

What happens though if the acceleration changes over time? Maybe we start with an acceleration A0, then after a period of time P0 a force changes it to A1, then after P1 it changes to A2, and then to A3, like in the following diagram.

但是,如果加速度随时间变化会怎样? 也许我们从加速度A0开始,然后在一段时间P0之后 ,力将其更改为A1 ,然后在P1之后将其更改为A2 然后更改为A3 ,如下图所示。

acceleration looks like an Observable, doesn’t it? Each event represents a change in the acceleration of the MobileObject (that is, the fact that a new force has been applied to MobileObject).

加速看起来像是可观察的,不是吗? 每个事件都表示MobileObject的加速度发生了变化(也就是说,已经向MobileObject施加了新的力量)。

Knowing A0 we can calculate the speed and position of MobileObject for the period P0 using an observable dyn0, built according to the logic described above. When the acceleration changes, we can still calculate speed and position, but we have to abandon dyn0 and switch to a new Observable dyn1, which is built with the same logic as dyn0, but now using the new acceleration A1. The same switching is repeated when acceleration becomes A2 and then A3.

知道了A0,我们就可以使用可观测的dyn0来计算周期P0的MobileObject的速度和位置,该dyn0是根据上述逻辑构建的。 当加速度发生变化时,我们仍然可以计算速度和位置,但是我们必须放弃dyn0switch到新的Observable dyn1 ,它与dyn0具有相同的逻辑但是现在使用的是新的加速度A1 。 当加速度变为A2然后变为A3时,将重复相同的切换。

This is where the operator switchMap comes in handy. Via switchMap we can transform the acceleration observable into a new version of the dynamics observable. It can receive a new value emitted by acceleration, start off a new observable dynX, complete the previous observable dynX-1, and emit all the events generated by the various observables of type dynX which it has spun off during this processing. The following diagram illustrates the switchMap mechanism.

这是操作员switchMap派上用场的地方。 通过switchMap我们可以将可观察到的加速度转换为可观察到的动力学的新版本。 它可以接收由加速度发射的新值,启动新的可观察的dynX,完成先前的可观察的dynX-1 ,并发射由dynX类型的各种可观察的对象生成的所有事件,该事件在此处理过程中已分离出来。 下图说明了switchMap机制。

现在欢迎主题先生-MobileObject的加速踏板 (Welcome now Mr. Subject — the accelerator pedal of MobileObject)

For this to work, we need to create the accelerator pedal. This is a mechanism that allows external controllers to change the acceleration of MobileObject.

为此,我们需要创建油门踏板。 这是一种允许外部控制器更改MobileObject加速度的机制。

Acceleration needs to be controlled, so we need a command mechanism.

加速需要控制,因此我们需要一种命令机制。

To change the acceleration of MobileObject, we need to cause the acceleration observable to emit events when the controller decides so. If we need to control when an Observable emits, we need to look at Subject, another type provided by RxJs.

要更改MobileObject的加速度,我们需要在控制器决定时使可观察到的加速度发出事件。 如果我们需要控制Observable何时发出,则需要查看Subject ,这是RxJs提供的另一种类型。

A Subject is an Observable which offers the following methods:

主题是可观察对象,它提供以下方法:

  • next(val) : emits an event with val as value

    next(val) :发出一个以val为值的事件

  • error() : terminates itself with an error

    error() :以错误终止自身

  • complete() : completes gracefully

    complete() :正常完成

So, if we want to change the acceleration over time, we can create the acceleration observable as a Subject, and then use the next() method to emit the event when needed.

因此,如果我们想随时间改变加速度,则可以创建可观察到的加速度作为Subject,然后在需要时使用next()方法发出事件。

将所有内容包装到MobileObject类中 (Wrap everything into the MobileObject class)

Now that we have all the parts required, we have just to assemble them into a coherent MobileObject class.

现在我们已经拥有了所需的所有部分,我们只需要将它们组装成一个连贯的MobileObject类。

In a nutshell, this is how a MobileObject is modeled in a reactive world. There are:

简而言之,这就是在React世界中对MobileObject建模的方式。 有:

  • some observables, dynamicsX and dynamicsY from the example, that emit data about its dynamics along the various dimensions of space (in the above example just 2, X and Y, in a bi-dimensional plan)

    该示例中的一些可观察对象, dynamicsXdynamicsY ,它们沿空间的各个维度发出有关其动力学的数据(在上面的示例中,二维平面中只有2,X和Y)

  • some subjects, accelerationX and accelerationY from the example, that allow controllers to change acceleration along the various dimensions

    示例中的一些主题, accelerationXaccelerationY ,允许控制器沿各个维度更改加速度

  • an internal clock that establishes the frequency of the time intervals

    建立时间间隔频率的内部时钟

In a 2 dimensional space, we have 2 different observables emitting the variation of space. Such observables need to share the same clock if we want a coherent movement. And clock is in itself an observable. So that they can share the same observable, we have added the share() operator at the end of the buildClock() function we described previously.

在二维空间中,我们有2个不同的可观测量,它们发出了空间的变化。 如果我们要进行连贯的运动,这些可观测对象需要share相同的时钟时钟本身就是可观察的。 为了使它们可以共享相同的可观察对象,我们在前面描述的buildClock()函数的末尾添加了share()运算符。

最后接触:刹车 (Final touch: brake)

Let’s look at this very simplistically. If you want to stop or slow down a car that moves with velocity V0, you have to apply to the car an acceleration in the direction opposite that of its velocity.

让我们非常简单地看一下。 如果要停止或减速以速度V0移动的汽车,则必须向汽车施加与速度相反的加速度。

After a period of time, the velocity of the car will become 0, and at that point no further acceleration is applied to the car.

一段时间后,轿厢的速度将变为0,并且此时不再对轿厢施加进一步的加速度。

To obtain a brake effect, we therefore have to know the direction of the MobileObject and stop the negative acceleration when the MobileObject reaches velocity 0.

因此,为了获得制动效果,我们必须知道MobileObject的方向,并在MobileObject达到速度0时停止负加速度。

Knowing the direction is easy. We have just to take the first event emitted by the dynamicsX or dynamicsY observable, depending on the axis we are interested in, and check if the velocity of the last event is positive or negative. The sign of the velocity is the direction.

知道方向很容易。 我们只需要根据所关注的轴来获取dynamicsXdynamicsY可观察到的第一个事件,并检查最后一个事件的速度是正还是负。 速度的标志是方向。

directionX = mobileObject.dynamicsX.take(1).map(dynamics => dynamics.vel > 0 ? 1 : -1)

directionX is an observable which emits only one event. The value emitted is 1 if the velocity is positive, -1 otherwise.

directionX是可观察的对象,仅发出一个事件。 如果速度为正,则发出的值为1,否则为-1。

So, when MobileObject receives the command to brake, all it has to do is to get the direction and apply an opposite acceleration, like this:

因此,当MobileObject接收到制动命令时,它要做的就是获取方向并施加相反的加速度,如下所示:

directionX.switchMap(   // BRAKE is a constant of acceleration when mobileObject brakes   dir => mobileObject.accelerationX.next(-1 * dir * BRAKE))

We are almost there. We just need to make sure that once the velocity reaches 0, or close to 0, we remove any acceleration. And this is how we can get what we want.

我们就快到了。 我们只需要确保一旦速度达到0或接近0,就可以消除任何加速度。 这就是我们可以得到想要的东西的方式。

directionX.switchMap(   // BRAKE is a constant of acceleration when mobileObject brakes   dir => {      mobileObject.accelerationX.next(-1 * dir * BRAKE);      return mobileObject.dynamicsX      // VEL_0 is a small value below which we consider vel as 0      .filter(dynamics => Math.abs(dynamics.vel) < VEL_0)      .do(() => mobileObject.accelerationX.next(0)      .take(1)   }).subscribe()

Here, after issuing the brake acceleration command, we simply select the first event of dynamicsX observable where the velocity is sufficiently small to be considered 0. Then we issue a command to apply an acceleration equal to zero. The last take(1) operator is added to make sure that we immediately unsubscribe, since the brake observable has completed its job.

在此,在发出制动加速度命令后,我们仅选择可观察到的动力学 X的第一事件,其中速度足够小以至于可以视为0。然后发出命令以施加等于0的加速度。 添加了最后一个take(1)运算符,以确保我们立即退订,因为可观察的制动器已完成其工作。

This code needs some refinement to work really smoothly, but it is enough to convey the basics of braking reactively.

该代码需要进行一些改进才能真正平稳地运行,但是足以传达被动制动的基础。

回到开始:动画 (Back to the start: animation)

All this may look good, but we still want to animate our MobileObject. For instance, we want to create an application where a user can issue acceleration commands via a 4-button console and see the MobileOject move accordingly.

所有这些看起来都不错,但是我们仍然要为MobileObject设置动画。 例如,我们要创建一个应用程序,用户可以在其中通过4按钮控制台发出加速命令,并查看MobileOject相应地移动。

Such an app acts as the controller of MobileObject and as the monitor to show the animation.

这样的应用程序充当MobileObject的控制器 ,并充当显示动画的监视器。

发出命令 (Issuing commands)

Controlling the movement of MobileObject means that we need to apply acceleration. The browser app can do this using the accelerationX subject provided by MobileObject, as shown in the following snippet.

控制MobileObject的运动意味着我们需要应用加速。 浏览器应用程序可以使用MobileObject提供的加速 X主题来执行此操作,如以下代码片段所示。

// mobileObject contains the instance we want to controlconst accelerationValue = 100;pAccX() {   mobileObject.accelerationX.next(accelerationValue);}releaseAccX() {   mobileObject.accelerationX.next(0);}

An acceleration of 100 is applied when the mouse button is down and acceleration is set to 0 when the mouse button is released, simulating the accelerator pedal.

当按下鼠标按钮时,将施加100的加速度;当释放鼠标按钮时,将加速度设置为0,以模拟油门踏板。

显示动画动作 (Show animated movement)

MobileObject exposes dynamicsX and dynamicsY, 2 Observables that continuously emit data about the movement along the respective axis (for example, deltaSpace, current velocity, acceleration along X and Y). So the browser app has to subscribe to them to receive this streams of events and change the position of MobileObject at every event emitted, as shown in this sample snippet:

MobileObject公开dynamicsXdynamicsY ,这两个Observables连续发出有关沿相应轴的运动的数据(例如,deltaSpace,当前速度,沿X和Y的加速度)。 因此,浏览器应用必须订阅它们才能接收事件流,并在发出的每个事件处更改MobileObject的位置,如以下示例代码所示:

interface Dynamics {deltaVel: number; vel: number; deltaSpace: number; space: number}const mobileObjectElement = document.querySelector('.mobileobj');mobileObject.dynamicsX.subscribe(   (dyn: Dynamics) => {     const currentPositionX = mobileObjectElement.style.left;     const deltaSpaceX = dyn.deltaSpace;     mobileObjectElement.style.left = currentPositionX + deltaSpace;   })

动画框架 (Animation Frame)

The browser works asynchronously, and it is not possible to predetermine when it is ready to display a new frame. The animation, or the simulation of movement, is provided by changing the position of an object over time. A smooth animation changes the position at every frame displayed by the browser.

浏览器是异步运行的,因此无法预先确定何时可以显示新的框架。 通过随时间改变对象的位置来提供动画或运动模拟。 平滑的动画会更改浏览器显示的每一帧的位置。

RxJs provides a Scheduler called animationFrame which wraps the requestAnimationFrame browser API. A Scheduler is a type of RxJs that controls when the events emitted by an observable really occur.

RxJs提供了一个名为animationFrame调度程序 ,该调度程序包装了requestAnimationFrame浏览器API。 调度程序是一种RxJ,它控制可观察对象发出的事件何时真正发生。

We can use animationFrame and the interval static method of Observable to create an observable that emits one event every time the browser is ready to display a new frame.

我们可以使用animationFrame和Observable的interval静态方法来创建一个observable,它在每次浏览器准备显示新帧时都发出一个事件。

Observable.interval(0, animationFrame)

Now we just need to add the length of time passed since the last frame to the events emitted by the this observable, and we have what we needed: an observable that emits every time the browser is ready to display a new frame with the amount of time passed since the last frame was displayed.

现在,我们只需要将自上一帧以来经过的时间添加到此observable发出的事件中,我们便有了所需的东西:每当浏览器准备显示一个新的帧时,此observable就会发出,其数量为自显示最后一帧以来经过的时间。

This is the new clock which we use in MobileObject to provide a stream of events relative to the movements (dynamicsX and dynamicsY). These movements are synchronized with when the browser is ready to show a new frame.

这是我们在MobileObject中使用的新时钟 ,用于提供与运动有关的事件流( dynamicsXdynamicsY )。 当浏览器准备显示新框架时,这些移动与同步。

You may have noticed that, in this last code example, the syntax has slightly changed. We are now using the “pipeable” operators. We did not use them before, since they don’t add anything to our reasoning. Still, it is worth introducing them since they represent new syntax you can use since RxJS 6.

您可能已经注意到,在上一个代码示例中,语法略有更改。 我们现在正在使用“管道”运算符。 我们以前没有使用过它们,因为它们不会在我们的推理中添加任何内容。 尽管如此,还是值得介绍它们,因为它们代表了自RxJS 6起可以使用的新语法。

You may also notice the defer function. This is an RxJs function that returns an Observable, but makes sure that the logic defined within the function passed as a parameter to defer is executed only when the Observable is subscribed.

您可能还会注意到defer功能。 这是一个RxJs函数,它返回一个Observable,但要确保仅在订阅Observable时,才执行在函数中定义为参数传递的逻辑以进行defer

This allows us to execute the buildClock() method at any time, maybe while initializing a UI component. It also allows us to be sure that the clock will start ticking only when subscribed and with the right timing. More specifically let startOfPreviousFrame = animationFrame.now(); will be executed only when the clock observable is subscribed.

这使我们可以在初始化UI组件时随时执行buildClock()方法。 它还使我们可以确保只有在订阅时并在正确的时间,时钟才会开始计时。 更具体地说, let startOfPreviousFrame = animationFrame.now(); 仅当订阅了可观察的时钟时才会执行。

最后但并非最不重要的一点,关于函数式编程风格的几句话 (Last but not least, a few words about the functional programming style)

At the beginning of our discussion, we talked about building the stream of data representing the movement of MobileObject over time. We called this the dynamics observable, and used the following transformation logic:

在我们的讨论开始时,我们讨论了构建表示MobileObject随时间变化的数据流。 我们称其为动态可观察的,并使用了以下转换逻辑:

map(dT => {  const dV = A * dT;  vel = vel + dV;  const dS = vel * dT + A / 2 * dT * dT;   space = space + dS;  return {dV, vel, dS, space};})

This assumes that we have defined the variables vel and space somewhere so that they are visible within the scope of the function passed as a parameter to the map operator.

假设我们已经在某个地方定义了变量velspace ,以便它们在作为参数传递给map运算符的函数范围内可见。

The first solution that might come to mind for a traditional OO programmer is to define such variables as properties of the MobileObject class. But this would mean storing state information at the object level that should only be changed by the transformation defined within the map operator shown above.

传统OO程序员可能想到的第一个解决方案是将此类变量定义为MobileObject类的属性。 但这意味着将状态信息存储在对象级别,该状态信息只能通过上面显示的map运算符中定义的转换来更改。

If you make this state information accessible to potentially any piece of logic within MobileObject, you risk changing it by mistake, making the entire object inconsistent. Plus, any time such state is changed, we have to think about other parts of logic that are potentially relying on this state. We need to consider the consequences of such dependencies, which sometimes may be pretty well hidden.

如果使此状态信息可能对MobileObject中的任何逻辑都可访问,则可能会错误地更改它,从而使整个对象不一致。 另外,无论何时更改这种状态,我们都必须考虑可能依赖于此状态的逻辑的其他部分。 我们需要考虑这种依赖的后果,有时可能会掩盖得很深。

Here is where functional programming comes to our rescue.

这是函数式编程为我们提供帮助的地方。

更高级别的功能 (Higher level functions)

A higher level function is a function which returns a function. The name might reminds you of higher level observables, which are observables that emit other observables.

较高级别的函数是返回函数的函数。 该名称可能使您想起更高级别的可观察物,它们是发出其他可观察物的可观察物。

The dynamics observable of MobileObject can be built if we have the clock observable and we know the acceleration A. So we can say that dynamics is function of the clock observable and the acceleration value A.

如果我们可以观察时钟并且知道加速度A ,则可以构建MobileObject的可观察动力学 。 因此,可以说动力学是可观察到的时钟和加速度值A的函数。

We can also create a function, dynamicsF, which returns a function dF. It in turn, when called, returns the dynamics observable, as shown in the snippet below.

我们还可以创建一个函数dynamicsF ,该函数返回一个函数dF。 依次调用时,它返回可观察到的动态 ,如下面的代码片段所示。

Notice that in dynamicsF, we have defined the variables vel and space, which are perfectly visible from within dF, making our code consistent and correct.

注意,在dynamicsF中,我们定义了变量velspace ,这些变量在dF中是完全可见的,从而使我们的代码一致且正确。

If we have a variable clock where we store the clock observable and a variable acc where we store the value of the acceleration A, we can use the function dynamicsF, which we have just defined, to build our dynamics observable as shown in the following snippet.

如果我们有一个可变的clock存储可观测的时钟,而一个可变的acc存储加速度A的值,则可以使用刚刚定义的dynamicsF函数来构建可观测的动态 ,如以下代码片段所示。 。

const dynFunction = dynamicsF();const dynamics = dynFunction(clock, A);

The key point is that now dynFunction contains in its internals the variables vel and space. It stores them internally in its own state, a state which is not visible to anything outside the function.

关键是现在dynFunction内部包含变量velspace 。 它在内部以它们自己的状态存储它们,该状态对于函数外部的任何对象都不可见。

Assuming that dynamicsF is a method of MobileObject class, the final version of the code that creates the dynamics observable in MobileObject constructor can be written as

假设dynamicsF是MobileObject类的一种方法,则可将创建可在MobileObject构造函数中观察到的动态的代码的最终版本编写为:

const dfX = this.dynamicsF();this.dynamicsX = this.accelerationX                     .swithMap(a => dfX(this.clock, a));

In doing so, we have confined the state information about current velocity and space into the function dfX. We’ve also removed the need to define properties for current velocity and space in MobileObject. And we have improved reuse, since dynamicsF() does not have any reference to any axis and can be used to calculate both dynamicsX and dynamicsY via function composition.

这样,我们将有关当前速度和空间的状态信息限制在函数dfX 。 我们也不再需要为MobileObject中的当前速度和空间定义属性。 而且,由于dynamicsF()没有对任何轴的引用,并且可以用于通过函数组合来计算dynamicsXdynamicsY ,因此我们改善了重用性。

By applying a functional programming style (in this case higher isolation), we have gained higher security for our code and higher reuse.

通过应用函数式编程风格(在这种情况下,更高的隔离度),我们为代码获得了更高的安全性以及更高的重用性。

结论 (Conclusion)

It has been a pretty long journey. We have seen the use of some of the most important RxJs operators and how Subjects can be handy. We have seen also how to use a functional programming style to increase the security of our code as well as its reusability.

这是一段相当长的旅程。 我们已经看到了一些最重要的RxJs运算符的用法以及如何方便使用Subject。 我们还看到了如何使用函数式编程风格来提高代码的安全性和可重用性。

I hope I’ve been able to show how, using a reactive thinking approach to this problem, it is possible to build a software solution which very naturally mirrors a real life model for objects that are remotely controlled.

我希望我已经能够展示出使用React性思考方法解决此问题的方法,从而有可能构建一个软件解决方案,该解决方案非常自然地反映远程控制对象的真实模型。

Any time you have to face a problem where time and asynchronicity play a role, then reactive thinking supported by reactive libraries such as RxJs can lead you to a simpler and more solid design. In this world of constant connectivity, the cloud, non-blocking platforms, and microservices, time and asynchronicity are going to play an ever-increasing role.

每当您不得不面对时间和异步性都起着作用的问题时,React性库(例如RxJs)所支持的React性思维将使您的设计更简单,更可靠。 在这个不断连接的世界中,云,无阻塞平台和微服务,时间和异步性将扮演越来越重要的角色。

If you liked what you have just read, you may be interested in , where I describe how to build a distributed system to control and display in action multiple MobileObjects in a distributed environment.

如果您喜欢刚刚阅读的内容,那么您可能也有兴趣 ,在此我将介绍如何构建一个分布式系统来控制和在分布式环境中实际显示多个MobileObject。

The entire code base .

完整的代码库 。

I want to thank Ben Lesh who inspired this piece with .

我要感谢本·莱什(Ben Lesh) 启发了这篇文章。

翻译自:

rxj热血江hsf湖私服

转载地址:http://eikzd.baihongyu.com/

你可能感兴趣的文章
java 中打印调用栈
查看>>
开发 笔记
查看>>
数据挖掘算法比赛 - 简单经验总结
查看>>
生成商户订单号/退款单号
查看>>
使用Android OpenGL ES 2.0绘图之六:响应触摸事件
查看>>
我们过去几年做对了哪些事
查看>>
ubuntu 16.04LTS
查看>>
javascript深入理解js闭包
查看>>
Oracle的安装
查看>>
Android Socket连接PC出错问题及解决
查看>>
Android Studio-—使用OpenCV的配置方法和demo以及开发过程中遇到的问题解决
查看>>
第2天线性表链式存储
查看>>
python自动化测试-D11-学习笔记之一(yaml文件,ddt)
查看>>
mysql存储过程使用游标循环插入数据
查看>>
Ubuntu 12.04 添加新用户并启用root登录
查看>>
20145309信息安全系统设计基础第9周学习总结上
查看>>
c# 字段、属性get set
查看>>
td内容超出隐藏
查看>>
Spring CommonsMultipartResolver 上传文件
查看>>
Settings app简单学习记录
查看>>