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 here.

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

If you like this, this is a second article 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 brakesdir => 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 brakesdir => {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主题来执行此操作,如以下代码片段所示。

<button id="positiveAccX" (mousedown)="pAccX()" (mouseup)="releaseAccX()"/>// mobileObject contains the instance we want to control
const 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 reading also this article, 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 can be found here.

完整的代码库可以在这里找到 。

I want to thank Ben Lesh who inspired this piece with one of his talks.

我要感谢本·莱什(Ben Lesh) ,他的演讲之一启发了这篇文章。

翻译自: https://www.freecodecamp.org/news/thinking-reactively-how-to-animate-with-movement-objects-using-rxjs-692518b6f2ac/

rxj热血江hsf湖私服

rxj热血江hsf湖私服_如何使用RxJ进行React性思考和动画化移动对象相关推荐

  1. react 消息队列_具有AkkaReact流的React队列

    react 消息队列 React性流是最近宣布的一项计划,旨在在JVM上为具有内置背压的异步流处理创建标准. 该工作组由Typesafe,Red Hat,Oracle,Netflix等公司组成. 早期 ...

  2. react 组件引用组件_动画化React组件

    react 组件引用组件 So, you want to take your React components to the next level? Implementing animations c ...

  3. 计算机动画电影英语翻译,英语翻译_推荐!最适合练口语的10部动画电影_沪江英语...

    小编导读:在学习英语的过程中,有不少人通过各种影视剧来提升英语语感.增加词汇量还有积累地道表达.尤其是那些英语动画电影,因为发音清晰.情感丰富以及大量的日常对话深受大家欢迎,但是要注意的是,并不是所有 ...

  4. 前端面试——初(H)入(T)江(M)湖(L)

    前言 如果觉得文章对您有帮助记得给个 Star,你的 star 是我动力的源泉.github 地址 正所谓面试如考试,考试如战场.战场上必将刀光剑影. 阅文档,刷试题,只求简历能入围 会面试官,戏 H ...

  5. mysql 循环查询依赖_关于循环依赖 - _江左梅郎_的个人空间 - OSCHINA - 中文开源技术交流社区...

    1:不要出现相互依赖  或者循环依赖, 最好是单向依赖   (之前pb出现循环依赖) 2:api之间不相互依赖, 只是实现之间相互依赖api,这样就不会出现循环依赖了 比如  现在的需求是  查询达人 ...

  6. 语义分析 文本矛盾点解析_关于解析文本的几点思考

    语义分析 文本矛盾点解析 Yesterday I wrote about three course modules in Oslo, and the fact that most of the pre ...

  7. 团队管理新思考_需要一个新的空间来思考讨论和行动

    团队管理新思考 andrew wong安德鲁·黄 Follow跟随 Sep 4 九月4 There is a need for a new space to think, discuss, and a ...

  8. 物联网传感器_基于传感器的物联网预测性维护,为什么必须对机器进行数字信号处理...

    物联网传感器 The industrial plants consist of several types of assets. Sensor based IoT is employed for as ...

  9. 动态半导体ram依据什么存储信息_半导体RAM是易失性RAM,而静态RAM中的存储信息是不易失的。...

    物流性应可得指标衡量以下绩效进行述的三个,半导即(. 体R态物流象是管理的对. 特别要注意,易失性它的选择滤波考虑考虑器时电容容量耐压又要即要,造成器损低于电压电容坏耐压将会实际使用.被王性德龙江之行 ...

  10. react hooks使用_如何使用Hooks将React类组件转换为功能组件

    react hooks使用 by Balaganesh Damodaran 通过Balaganesh Damodaran 如何使用Hooks将React类组件转换为功能组件 (How to conve ...

最新文章

  1. 后悔没早知道这些Python特性
  2. 软件工程综合实践阶段小结(2)
  3. WebDev.WebServer 学习
  4. vue的this.$set的作用
  5. 香港计算机mphil申请成功案例,香港稀有CS MPhil录取:申请也有起死回生的惊喜...
  6. cron java_cron表达式
  7. 线性排序算法-堆排序 (2)
  8. 计算机组成与设计试题,计算机组成原理试题
  9. React Native 程序部署至 iOS 应用商店之前需要的配置和如何生成 release 版本的 APK 包
  10. 我最近在看什么——《蛤蟆先生去看心理医生》
  11. 全排列牛客和L46,L47
  12. SMSS打开界面闪退的解决方案
  13. Python 安装PyQt5失败:Permission denied:d3dcompiler_47.dll
  14. 成都理工大学计算机考研经历,09计算机考研的小小体会~
  15. Opencv像素值的存储及访问机制
  16. linux没有cpufreq目录,为什么数值计算的时候 cpu 到不了最大频率?
  17. 学习型组织将何去何从?
  18. Java 8 辣么大(lambda)表达式不慌之—–(五)示例-Collectors中的统计、分组、排序等
  19. 安装 VMware 15出现的小问题
  20. C++ cin输入空格

热门文章

  1. win10电脑显示未连接网络连接到服务器,win10系统未识别网络无法连接到internet的解决方法...
  2. 树莓派ubuntu mate 修改屏幕解析度为800x480
  3. fastjson解析json文本
  4. html5背景泡泡,HTML5 canvas梦幻圆形泡泡动画背景特效
  5. 遥感数据相关资源获取
  6. 移动通信技术的发展历程初
  7. 注塑成型工艺流程四大知识点总结
  8. IdPop3 出现 Max line length exceeded.的解决方法
  9. 采样频率变化时,滤波器的性能会变差吗?
  10. OAuth2授权方式