How to prioritize asynchronous tasks in RxJS? - rxjs

This came up in the context of an app that runs some relatively costly computations in a web worker, and has to prioritize some of these computations over others (for example those affecting the UI over those needed to sync data with the server).
As a simple example, suppose I have some observables that asynchronously process values emitted by a source observable:
import { interval } from 'rxjs';
import { delay, share } from 'rxjs/operators';
const a = interval(500).pipe(share());
const b = a.pipe(delay(0), map(value => value + 10));
const c = b.pipe(delay(0), map(value => value + 100));
const d = a.pipe(delay(0), map(value => value + 1000));
I would like the values to be processed in a specific order, for example a, b, d, c, so b has priority over d, and d over c.

Use concat
const result = concat(a, b, d, c)

Related

Polling using expand in rxjs gives an unexpected result

I am trying to setup a poller using expand but the behavior is not what I want
https://stackblitz.com/edit/rxjs-finalize-unsubscribe-6xy2yb?file=index.ts
checkExistence produces a random boolean - With the expand, I expect a recursive delayed call of the same checkExistence function producing random booleans every 5 seconds (after one initial call).
I also expect the stop to kick in 30seconds and stop the polling and 5 seconds later resume the random boolean stream. Any pointers will help.
Instead I get the same boolean value getting printed; also after the start is triggered, it produces batches of booleans together.
This isn't really a single question.
Assignment/function Invocation
A simplified example
The equals operator assigns a value to a variable. Function invocation returns a value
function inc(n){
return n + 1
}
const result = inc(5)
console.log(result); // 6
Here, the result holds the number value 6. Not a function that calculated 5 + 1. 5 + 1 only happens once here.
Now consider this function that randomly increments a number by either 1 or 2
function bump(n){
const nu = Math.random();
return nu < 0.5 ? n + 1 : n + 2
}
const result = bump(5);
console.log(result); // 7
console.log(result); // 7
console.log(result); // 7
console.log(result); // 7
console.log(result); // 7
Every time I print the result, it's the same. bump was only called once and only generated a random number once.
function bump(n){
const nu = Math.random();
return nu < 0.5 ? n + 1 : n + 2
}
console.log(bump(5)); // 7
console.log(bump(5)); // 7
console.log(bump(5)); // 6
console.log(bump(5)); // 7
console.log(bump(5)); // 6
Here, bump is called 5 times, and each time it generated a new random number.
Fixing checkExistence
There are two ways.
Instead of generating a value once and re-using it. Generate a new boolean in your poll every time you need one.
function checkExistencePoll(): Observable<boolean> {
const POLL = 5000;
return checkExistence().pipe(
expand(_ =>
timer(POLL).pipe(
switchMap(() => checkExistence()),
takeUntil(stop),
repeatWhen(() => start)
)
)
);
}
Have checkExistence return an observable that generates a new boolean on subscription
function checkExistence(): Observable<boolean> {
return defer(() => {
const nu = Math.random();
const rand = nu < 0.5;
console.log(nu, rand);
return of(rand);
});
}
Handling subscriptions
In your code, expand is generating an observable that subscribes to a subject ( called start) in order to decide when to repeat. Every observable expand creates does this. So each one repeats when start emits. You should expect batches of booleans equal to the number of concurrent observables you've created.

how to fast judge two double set intersect or not?

I want to fast judge two double set intersect or not.
problem
The element in set can be all range. The element in set are not ordered. Each set have 100,000+ element.
If exist a double a from set A, a double b from set B, a and b is very close,for example abs(a-b)<1e-6, we say set A and B intersect.
My way
calculate the range(lower bound and upper bound) of set_A and set_B
O(n), n is set's size
calculate range intersection rang_intersect of range_A and range_B
O(1)
if rang_intersect empty two set not intersect.
O(1)
if range_intersect not empty, find sub_set_A from set_A which in the range_intersect, find sub_set_B from set_B which in the range_intersect
O(n)
sort sub_set_A and sub_set_B
O(mlogm) m is sub_set_A's size
tranvers sub_set_A_sorted and sub_set_B_sorted by two pointer. find if exist element close, if exist two set intersect, if not, two set not intersect.
O(m)
My way can works, but I wonder if I can do faster.
Appendix
Why I want this:
Actually I am face a problem to judge two point set A & B collision or not. Each point p in point set have a double coordinate x,y,z. If exist a point a from point set A, a point b from point set B, a and b's coordinate very close, we say point set A and B collision.
In 3d case, we can define the order of point by first compare x then compare y, last compare z.
We can define the close that if all dimension's coordinate is close , the two point close.
This problem can convert to the problem above.
Some idea by gridding the space:
Let's take the point (1.2, 2.4, 3.6) with minimial distance required 1.
We may say that this point "touches" 8 unit cubes of R^3
[
(1.0, 2.0, 3.5)
(1.0, 2.0, 4.0)
(1.0, 2.5, 3.5) // 1 < 1.2 < 1.5
(1.0, 2.5, 4.0) // 2 < 2.4 < 2.5
(1.5, 2.0, 3.5) // 3.5 < 3.6 < 4
(1.5, 2.0, 4.0)
(1.5, 2.5, 3.5)
(1.5, 2.5, 4.0)
]
If two points are close to each other, their will be connected by some of their cube.
y
^
|
3 +---+---+
| | |
2.5+-------+---+---+
| a | | c | b |
2 +---+---+---+---+--->x
1 1.5 2
In example above in 2D plan, a is (1.2, 2.4).
Say b is (2.5, 2.4). b will touch the square (2,2), but a does not.
So they are not connected (indeed the min distance possible is (2.5-1.5===1).
Say c is (2.45, 2.4). c touches the square (1.5, 2). So is a. We check.
The main idea is to associate to each point its 8 cubes.
We can associate a uniq hash to each cube: the top level coordinate. e.g "{x}-{y}-{z}"
To check if A intersects B:
we build for each point of A its 8 hashes and store them in a hashmap: hash->point
for each point of B, we build the hashes, and if one of those exist in the hashmap we check if the corresponding points are in relation
Now consider
y
^
|
3 +---+---+
| a2| |
2.5+-------+
| a1| |
2 +---+---+
1 1.5 2
a2 and a1 's hashes will overlap on squares (1,2) and (1,2.5). So the hashmap is actually hash->points.
This implies that worst case could be O(n^2) if all the points land into the same cubes. Hopefully in reality they won't?
Below a code with irrelevant data:
(put 10**4 to avoid freezing the ui)
function roundEps (c, nth) {
const eps = 10**-nth
const r = (c % eps)
const v = (r >= eps / 2) ? [c-r+eps/2, c-r+eps] : [c-r, c-r+eps/2]
return v.map(x => x.toFixed(nth + 1))
}
function buildHashes (p, nth) {
return p.reduce((hashes, c) => {
const out = []
hashes.forEach(hash => {
const [l, u] = roundEps(c, nth)
out.push(`${hash},${l}`, `${hash},${u}`)
})
return out
},[''])
}
function buildMap (A, nth) {
const hashToPoints = new Map()
A.forEach(p => {
const hashes = buildHashes(p, nth)
hashes.forEach(hash => {
const v = hashToPoints.get(hash) || []
v.push(p)
hashToPoints.set(hash, v)
})
})
return hashToPoints
}
function intersects (m, b, nth, R) {
let processed = new Set()
return buildHashes(b, nth).some(hash => {
if (!m.has(hash)) return
const pts = m.get(hash)
if (processed.has(pts)) return
processed.add(pts)
return pts.some(p => R(p, b))
})
}
function d (a, b) {
return a.reduce((dist, x, i) => {
return Math.max(dist, Math.abs(x-b[i]))
}, 0)
}
function checkIntersection (A, B, nth=2) {
const m = buildMap(A, nth)
return B.some(b => intersects(m, b, nth, (a,b) => d(a, b) < 10**(-nth)))
}
// ephemeral testing :)
/*
function test () {
const assert = require('assert')
function testRound () {
assert.deepEqual(roundEps(127.857, 2), ['127.855', '127.860'])
assert.deepEqual(roundEps(127.853, 2), ['127.850', '127.855'])
assert.deepEqual(roundEps(127.855, 2), ['127.855', '127.860'])
}
function testD () {
assert.strictEqual(d([1,2,3],[5,1,2]), 4)
assert.strictEqual(d([1,2,3],[0,1,2]), 1)
}
function testCheckIntersection () {
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
assert(checkIntersection(A, B))
}
{
const A = [[1.213,2.178,1.254],[0.002,1.231,2.695]]
const B = [[10,20,30]]
assert(!checkIntersection(A, B))
}
{
const A = [[0,0,0]]
const B = [[0,0,0.06]]
assert(!checkIntersection(A, B, 2))
}
{
const A = [[0,0,0.013]]
const B = [[0,0,0.006]]
assert(checkIntersection(A, B, 2))
}
}
testRound()
testD()
testCheckIntersection()
}*/
const A = []
const B = []
for (let i = 0; i < 10**4; ++i) {
A.push([Math.random(), Math.random(), Math.random()])
B.push([Math.random(), Math.random(), Math.random()])
}
console.time('start')
console.log('intersect? ', checkIntersection(A, B, 6))
console.timeEnd('start')

How to save intermediate outputs into images on a multi-stage pipeline?

Say I have computation something like
Image resultA, resultB;
Func A, B, C, D, E;
Var x, y;
A(x,y) = C(x,y) * D(x,y);
B(x,y) = C(x,y) - D(x,y);
E(x,y) = abs(A(x,y)/B(x,y));
resultA(x,y) = sqrt(E(x,y));
resultB(x,y) = 2.f * E(x,y) + C(x,y);
How to define AOT schedule such that I can save resultA and resultB ?
E(x,y) is common to the computation of resultA and resultB.
Thank you in advance
If the results are the same size in all dimensions, you can return a Tuple:
result(x, y) = Tuple(resultA, resultB);
If they are not the same size, they can be added to a Pipeline and the Pipeline can be compiled to a filter that returns multiple Funcs.
See:
https://github.com/halide/Halide/blob/master/test/correctness/multiple_outputs.cpp

How to create an infinite Observable that produces random numbers at random intervals?

Given a function that generates random numbers, how would you create an infinite Observable that produces random numbers at random intervals?
function getRandomNumber() {
// Assume this function returns a random number, e.g. 198
}
function getRandomDelay() {
// Assume this function returns a random delay in ms, e.g. 2000
}
Here is an example of a desired Observable:
---198--------64-------3---2----------18-------> (indefinitely)
3ms 7ms 6ms 3ms 10ms
As an alternative, if you don't want to live in a confusing timeout-world, you could write this entirely as a stream:
// the stream
const randomizer$ = Rx.Observable.of("")
.switchMap(() => Rx.Observable
.timer(getRandomDelay())
.mapTo(getRandomNumber()))
.repeat();
// subscribe to it
randomizer$.subscribe(num => console.log("Random number after random delay" + num));
// your utility functions
function getRandomNumber() {
return ~~(Math.random() * 200)
}
function getRandomDelay() {
return Math.random() * 1000
}
Working example here: http://jsbin.com/zipocaneya/edit?js,console
Alternative: Create the random number first and then add a delay (if the time of execution does not matter)
// the stream
const randomizer$ = Rx.Observable.of("")
.switchMap(() => Rx.Observable
.of(getRandomNumber())
.delay(getRandomDelay()
)
.repeat();
// subscribe to it
randomizer$.subscribe(num => console.log("Random number after random delay" + num));
Additional note: Since there is no concurrency or async-operation going on outside of the stream, instead of switchMap you could just as well use concatMap or flatMap - in this case they all work the same.
const { Observable } = require("rxjs");
const ob = new Observable(sub => {
let timeout = null;
// recursively send a random number to the subscriber
// after a random delay
(function push() {
timeout = setTimeout(
() => {
sub.next(getRandomNumber());
push();
},
getRandomDelay()
);
})();
// clear any pending timeout on teardown
return () => clearTimeout(timeout);
});
ob.subscribe(console.log);
Would you take a look at https://github.com/cszredwan/crazyObservable?
I am sure it gonna be of help, this is a custom observable to emit (randomly!) a fixed amount of data over a fixed time horizon.
The problem with your question is that randomly is being used with imprecise meaning. crazyObservable draws a single stream of data of all possibilities of emitting data over the time horizon specified. Of course, you can concat multiples instances of crazyObservable to get desired behavior.
In RxPY 3.0 you can use the following construction:
res = rx.generate(0, lambda x: True, lambda x: x + 1).pipe(
ops.map(lambda x: rx.timer(random.random() * 0.4).pipe(ops.map(lambda y: x))),
ops.merge(max_concurrent=1),
ops.map(lambda x: {'count': x, 'value': random.randint(0, 5)}))
This produces an infinite stream of random integers between 0 and 5 at random times with interarrival time uniformly distributed on [0, 0.4].
In RxPY 3.0, operations like switchmap or concatmap are not implemented (as in #olsn's reply). The concat_all operation can be achieved by merge with max_concurrent=1.
Edit:
rx.generate(0, lambda x: True, lambda x: x + 1)
is blocking. Using an infinite vanilla python generator such as
import itertools
r = rx.from_iterable(_ for _ in itertools.count(start=0, step=1))
is also blocking. You can add some scheduler e.g.
from rx.scheduler.eventloop import AsyncIOScheduler
from rx.scheduler import ThreadPoolScheduler
import multiprocessing
scheduler = AsyncIOScheduler(asyncio.get_event_loop())
# scheduler = ThreadPoolScheduler(multiprocessing.cpu_count()) # alternatively
res = rx.generate(0, lambda x: True, lambda x: x + 1).pipe(
ops.map(lambda x: rx.timer(random.random() * 0.4).pipe(ops.map(lambda y: x))),
ops.merge(max_concurrent=1),
ops.map(lambda x: {'count': x, 'value': random.randint(0, 5)}),
ops.subscribe_on(scheduler)
)

Combining two streams of numbers using linq or rx

I was playing around with linq/rx and wanted to do something simple, create a stream that listens to two different streams of numbers and when either number stream gets a new value, output the sums.
I tried the below:
static void Main(string[] args)
{
var numSrcA = new Subject<int>();
var numSrcB = new Subject<int>();
var resultsLinq = from a in numSrcA
from b in numSrcB
select a + b;
var resultsRx = numSrcA.SelectMany(a => numSrcB, (a, b) => a + b);
resultsLinq.Subscribe(r => Console.WriteLine("Linq: " + r));
resultsRx.Subscribe(r => Console.WriteLine("Rx: " + r));
numSrcA.OnNext(1);
numSrcB.OnNext(2);
numSrcA.OnNext(3);
Console.ReadLine();
}
Output:
Linq: 3
Rx: 3
I was expecting:
Linq: 3
Rx: 3
Linq: 5
Rx: 5
Though it seems that the new sum will only be computed when numSrcB gets a new value (in which case it sums that new value against all existing values in numSrcA).
Can this be changed so that an addition to either numSrc will recompute the sums?
Short answer, you want a combinator that doesn't require new input on all streams to trigger the evaluation of the result - in other words, you want CombineLatest:
var resultsLinq2 = Observable.CombineLatest(numSrcA, numSrcB, (a,b) => a+b);
resultsLinq2.Subscribe(r => Console.WriteLine("Linq2: " + r));
Results in:
Linq2: 3
Linq: 3
Rx: 3
Linq2: 5

Resources