Can RxJS be used in a pull-based way? - stream

The examples in the RxJS README seem to suggest we have to subscribe to a source. In other words: we wait for the source to send events. In that sense, sources seem to be push-based: the source decides when it creates new items.
This contrasts, however, with iterators, where strictly speaking new items need only be created when requested, i.e., when a call is made to next(). This is pull-based behavior, also known as lazy generation.
For instance, a stream could return all Wikipedia pages for prime numbers. The items are only generated when you ask for them, because generating all of them upfront is quite an investment, and maybe only 2 or 3 of them might be read anyway.
Can RxJS also have such pull-based behavior, so that new items are only generated when you ask for them?
The page on backpressure seems to indicate that this is not possible yet.

Short answer is no.
RxJS is designed for reactive applications so as you already mentioned if you need pull-based semantics you should be using an Iterator instead of an Observable. Observables are designed to be the push-based counterparts to the iterator, so they really occupy different spaces algorithmically speaking.
Obviously, I can't say this will never happen, because that is something the community will decide. But as far as I know 1) the semantics for this case just aren't that good and 2) this runs counter to the idea of reacting to data.
A pretty good synopsis can be found here. It is for Rx.Net but the concepts are similarly applicable to RxJS.

Controlled observable from the page you referenced can change a push observable to pull.
var controlled = source.controlled();
// this callback will only be invoked after controlled.request()
controlled.subscribe(n => {
log("controlled: " + n);
// do some work, then signal for next value
setTimeout(() => controlled.request(1), 2500);
});
controlled.request(1);
A truly synchronous iterator is not possible, as it would block when the source was not emitting.
In the snippet below, the controlled subscriber only gets a single item when it signals, and it does not skip any values.
var output = document.getElementById("output");
var log = function(str) {
output.value += "\n" + str;
output.scrollTop = output.scrollHeight;
};
var source = Rx.Observable.timer(0, 1000);
source.subscribe(n => log("source: " + n));
var controlled = source.controlled();
// this callback will only be invoked after controlled.request()
controlled.subscribe(n => {
log("controlled: " + n);
// do some work, then signal for next value
setTimeout(() => controlled.request(1), 2500);
});
controlled.request(1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.2/rx.all.js"></script>
<body>
<textarea id="output" style="width:150px; height: 150px"></textarea>
</body>

I'm quite late to the party, but it's actually very simple to combine generators with observables. You can pull a value from a generator function by syncing it with a source observable:
const fib = fibonacci()
interval(500).pipe(
map(() => fib.next())
)
.subscribe(console.log)
Generator implementation for reference:
function* fibonacci() {
let v1 = 1
let v2 = 1
while (true) {
const res = v1
v1 = v2
v2 = v1 + res
yield res
}
}

Related

Differentiating Mode 2 Form 1 from Mode 2 Form 2 on XA CD-ROMs?

I'm developing a library for reading CD-ROMs and the ISO9660 file system.
Long story short, pretty much everything is working except for one thing I'm having a hard time figuring out how it's done:
Where does XA standard defines differentiation among Mode 2 Form 1 from Mode 2 Form 2?
Currently, I am using the following pseudo-code to differentiate between both forms; albeit it's a naive heuristic, it does work but well, it's far from ideal:
var buffer = ... // this is a raw sector of 2352 bytes
var m2F1 = ISector.Cast<SectorMode2Form1>(buffer);
var edc1 = EdcHelper.ComputeBlock(0, buffer, 16, 2056);
var edc2 = BitConverter.ToUInt32(m2F1.Edc, 0);
var isM2F1 = edc1 == edc2;
if (isM2F1) return CdRomSectorMode.Mode2Form1;
// NOTE we cannot reliably check EDC of M2F2 since it's optional
var isForm2 =
m2F1.SubHeaderCopy1.SubMode.HasFlag(SectorMode2Form1SubHeaderSubMode.Form2) &&
m2F1.SubHeaderCopy2.SubMode.HasFlag(SectorMode2Form1SubHeaderSubMode.Form2);
if (isForm2) return CdRomSectorMode.Mode2Form2;
return CdRomSectorMode.Mode2Formless;
If you look at some software like IsoBuster, it appears to be a track-level property, however, I'm failing to understand where the value would be read from within the track.
I'm actually doing something similar in typescript for my ps1 mod tools. It seems like you actually probably have it correct here, since I'm going to assume your HasFlag check is checking position bit position 6 of the subheader. If that flag is set, you are in form 2.
So what you probably want something like:
const sectorBytes = new Uint8Arrray(buffer);
if (sectorBytes[0x012] & 0x20) === 0x20) {
return CdRomSectorMode.Mode2Form2;
} else {
return CdRomSectorMode.Mode2Form1;
}
You could of course use the flag code you already have, but that would require you to use one of the types first to get that. This just keeps it generic bytes and checks the flag, then returns the relevant mode.

How to modify data that is fetched from an observable?

I want to fetch data from an api(Movie lists with various information). I'm fairly new to observables, and keep getting confused. So the thing that I want to achieve is concatenate the base path with poster path (this I get from the api) and retrieve the poster to display in the view.
I'm able to fetch name of the movies from the api but I'm unable to concatenate the base URL to poster path. Any help or a direction on how to work with observables is really appreciated.
Service that I'm using to fetch data
export class MoviesService {
private _url: string = 'https://api.themoviedb.org/3/movie/popular?api_key=<<API KEY>>&language=en-US&page=2'
constructor(private http: HttpClient) { }
getmovies(): Observable<IMovie[]> {
return this.http.get<IMovie[]>(this._url);
}
}
I want to understand how to manipulate data that is fetched via an observable.
From what I understand, you are looking for rxjs operators, specifically for "Transforming" operators, found at:
https://www.learnrxjs.io/operators/transformation/
With that you can do things with the data your Observable emits, whenever it does.
Assuming the structure of your IMovie something like the following should get you on the right track.
getmovies(): Observable<{connectedURL: string}[]> {
return this.http.get<IMovie[]>(this._url).pipe(
map( (movies:IMovie[]) => {
result = [];
movies.forEach( (movie) => {
result.push(movie.getBaseURL() + movie.getPosterURL());
}
return result;
})
);
}
All this does is taking your array of Movies and create a new Array with your connected URLs.
So your getMovies() returns your modified list.
Of course your could do the map()-Operator somewhere else in your logic or even create some other kind of object with the info of your IMovie[].
I hope this is of any help to do.
Starting out with Observables and Operators is quite a big thing, so you should definetly check the link above out.
Feel free to correct me :)
Concat
emit the emissions from two or more Observables without interleaving them1
The Concat operator concatenates the output of multiple Observables so that they act like a single Observable, with all of the items emitted by the first Observable being emitted before any of the items emitted by the second Observable (and so forth, if there are more than two).
Concat waits to subscribe to each additional Observable that you pass to it until the previous Observable completes. Note that because of this, if you try to concatenate a “hot” Observable, that is, one that begins emitting items immediately and before it is subscribed to, Concat will not see, and therefore will not emit, any items that Observable emits before all previous Observables complete and Concat subscribes to the “hot” Observable.
RxJs Examples for Concat is:
Example 1: Basic concat usage with three observables
// RxJS v6+
import { of, concat } from 'rxjs';
concat(
of(1, 2, 3),
// subscribed after first completes
of(4, 5, 6),
// subscribed after second completes
of(7, 8, 9)
)
// log: 1, 2, 3, 4, 5, 6, 7, 8, 9
.subscribe(console.log);
Example 2: concat with delayed observable
// RxJS v6+
import { of, concat } from 'rxjs';
import { delay } from 'rxjs/operators';
concat(
of(1, 2, 3).pipe(delay(3000)),
// after 3s, the first observable will complete and subsquent observable subscribed with values emitted
of(4, 5, 6)
)
// log: 1,2,3,4,5,6
.subscribe(console.log);

Origin of files `holoclient.js` and `holoclient.map` in holochain application?

In https://github.com/holochain/holochat-rust, how are the files ui/holoclient.js and ui/holoclient.map obtained ?
Also, is there any official documentation about that that I missed and is this still the way to get a UI to talk to the holochain container ?
ui/holoclient.js is a tiny library that makes it much easier to talk to a running Holochain app instance. The current way of connecting your GUI to an instance is a JSON-RPC-like process via a local WebSocket connection. It's intended as a nice wrapper to make zome function calls feel like local, in-browser function calls. Documentation is currently very light, but it shouldn't take much to figure out how it's supposed to work using the example. In a nutshell:
const url = 'ws://localhost:3000/'
window.holoclient.connect(url).then(({call, close}) => {
document.getElementById('form').addEventListener('submit', e => {
e.preventDefault()
// First, get a list of locally running Holochain instances...
call('info/instances')().then(info => {
// Now that we have instance info, we can make zome calls into any of them
// by referring to them by DNA hash (and agent ID) as specified in our
// container config.
// Search for the instance we're looking for, given known DNA and agent
// hashes.
const matchingInstances = Object.entries(info)
.find(([id, value]) => value.dna === 'blog_dna_hash' && value.agent === 'my_agent_hash')
const instance = getInstance(info, 'the_dna_hash', 'the_agent_hash')
const content = document.querySelector('#message').value
// Make another zome call now
call(instance, 'blog', 'main', 'create_post')({
content: content
})
})
})
})
It's written in TypeScript, which would mean that ui/holoclient.map is a 'source map', a file which maps line numbers in the compiled JavaScript file to line numbers in the original TypeScript source. Both Chrome and Firefox look for and use those source maps when you're debugging your JS.

Grails executor plugin callAsync block always taking the same values

I'm trying to use the executor plugin in grails but I'm having a problem which I am not able to solve.
Basically, i have a list of links that I want to crawl and I was having an issue where it was always crawling the same ones, so I simplified my example to this:
List offerLinks = getOfferLinks(parser)
offerLinks.each{println it}
List futures = new Vector()
for (def link : offerLinks) {
def future = callAsync {
return link
}
futures.add(future)
}
futures.each{println "FUTURE " + it.get()}
This is what gets printed in the console
bt-ofrd-acciona-6633344.htm?
bt-ofrd-celiasiffredi-293068.htm?
bt-ofrd-clahubiz-92924.htm?
bt-ofrd-haruko-1672632.htm?
FUTURE bt-ofrd-clahubiz-92924.htm?
FUTURE bt-ofrd-haruko-1672632.htm?
FUTURE bt-ofrd-haruko-1672632.htm?
FUTURE bt-ofrd-haruko-1672632.htm?
The first 4 results are for the offerLinks.each{println it} code
The last 4 are for futures.each{println "FUTURE " + it.get()}
What I'm trying to find out is why putting those links in the callAsync block and retrieving them from the future objects make them take the last value, it seems like its replacing the already created future objects?
This piece of code is inside a service called by a controller.
I appreciate any help you can give me. Thanks
Update:
I'm thinking there is some kind of problem in the Java executor API... or maybe I'm don't fully understand how it really works?
Here is another test changing the code to use invokeAll:
def threadPool = Executors.newCachedThreadPool()
List offerLinks = getOfferLinks(parser)
List lista = new ArrayList()
for (enlace in offerLinks) {
println "link " + enlace
lista.add({enlace} as Callable)
}
def futures = threadPool.invokeAll(lista)
futures.each{println "FUTURE " + it.get()}
This is what gets printed
link /bt-ofrd-implementar-192996.htm?
link /bt-ofrd-cdonini-864908.htm?
link /bt-ofrd-hvtalent-1493932.htm?
link /bt-ofrd-dbak-1358120.htm?
link /bt-ofrd-hexacta-100072.htm?
link /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
FUTURE /bt-ofrd-ccibelli-457472.htm?
It looks to me like something odd is going on with the scope of variables defined outside the closure but referred to from inside, it's not "closing" properly. Does it work any better if you do
def threadPool = Executors.newCachedThreadPool()
List offerLinks = getOfferLinks(parser)
List lista = new ArrayList()
for (enlace in offerLinks) {
println "link " + enlace
lista.add(({ it }.curry(enlace)) as Callable)
}
def futures = threadPool.invokeAll(lista)
futures.each{println "FUTURE " + it.get()}
This should ensure that the right thing gets passed into the closure, and the closure itself doesn't need to refer to the externally-defined enlace variable directly.
This doesn't on its own explain why what you've already tried didn't work, but it might give you a workaround.
Edit: I didn't spot this before, but I now notice that you aren't declaring enlace in that for loop, so it's not a local variable and the closures are (correctly) referring to a single shared variable rather than "closing" over the value in a particular loop iteration. It should work if you use a construction like this instead:
def tasks = offerLinks.collect { link ->
println "link " + enlace
return ({ link } as Callable)
}
def futures = threadPool.invokeAll(tasks)
futures.each{println "FUTURE " + it.get()}
where the link variable is local to the collect closure, so the {...} as Callable will close over the correct value. The equivalent in terms of callAsync would be to use
List futures = offerLinks.collect { link ->
callAsync { link }
}
Is this better?
List offerLinks = getOfferLinks(parser)
offerLinks.each{println it}
List futures = new Vector()
for (def link : offerLinks) {
futures.add( callAsync {
return link
}
)
}
futures.each{println "FUTURE " + it.get()}
Sounds like the same phenonemon I just experienced, see this answer.
For me, the problem was that "The current thread's MDC is inherited by newly spawned threads". I don't know the why of that, so I can't tell why you may bump into the same problem - but maybe because you retrieve the links from a service?

Simulating mouse events in Actionscript 3

Given stage coordinates (x,y), I want to make my flash app behave just as if the user clicked at position (x,y). That is, something like
function simulateClick(x:Number, y:Number):void{
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, x, y)
stage.dispatchEvent(e);
}
I've found a bunch of pages talking about this kind of thing, and they all give solutions similar to the above. However, this isn't equivalent to the user clicking at (x,y). There are two problems:
The first is that e.stageX and e.stageY are both 0. I can't set them directly. The documentation says they are calculated when e.localX and e.localY are set, but this isn't happening when I set e.localX before dispatchEvent, nor in the event listener.
I could rewrite all of my event listeners with something like this:
var p:Point = e.target.localToGlobal(new Point(e.localX, e.localY));
Is this the only option?
The second problem is that my event listeners are registered with children of stage, not stage itself. So I need to find out what target to call dispatchEvent on. Clearly Flash is capable of determining what the target should be, ie which object owns the topmost pixel visible at position (x,y), because it does so when the user actually clicks. Is there an easy way to get at this information, or should I just write my own recursive function to do the same thing? I'm using DisplayObjectContainer.getObjectsUnderPoint at the moment, but it's not quite right.
I'm writing in FlashDevelop, if that makes any difference.
e.stageX/Y is populated correctly for me... also getObjectsUnderPoint() seems to work fine. I'm assuming that the x/y values passed to simulateClick are global coordinates?
edit: as pointed out in the comments, the mouse event must be dispatched on InteractiveObject instances... modified the code accordingly.
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Point;
public function simulateClick(x:Number, y:Number):void
{
var objects:Array = stage.getObjectsUnderPoint(new Point(x, y));
var target:DisplayObject;
while(target = objects.pop())
{
if(target is InteractiveObject)
{
break;
}
}
if(target !== null)
{
var local:Point = target.globalToLocal(new Point(x, y));
var e:MouseEvent = new MouseEvent(MouseEvent.CLICK, true, false, local.x, local.y);
target.dispatchEvent(e);
}
}
public function addedToStage():void
{
var parent:Sprite = new Sprite();
stage.addChild(parent);
var child:Sprite = new Sprite();
child.name = 'child 1';
child.graphics.beginFill(0xff0000, 1);
child.graphics.drawRect(0, 0, 200, 200);
child.graphics.endFill();
var child2:Sprite = new Sprite();
child2.name = 'child 2';
child2.graphics.beginFill(0xff00ff, 1);
child2.graphics.drawRect(0, 0, 100, 100);
child2.graphics.endFill();
child2.x = 150;
child2.y = 150;
var bmpData:BitmapData = new BitmapData(80, 80, false, 0x00ff00);
var bmp:Bitmap = new Bitmap(bmpData);
bmp.name = 'bitmap';
child2.addChild(bmp);
parent.addChild(child);
parent.addChild(child2);
child2.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void
{
trace('target: ' + e.target.name);
trace('localX: ' + e.localX);
trace('localY: ' + e.localY);
trace('stageX: ' + e.stageX);
trace('stageY: ' + e.stageY);
});
simulateClick(190, 190);
}
Output:
target: child 2
localX: 40
localY: 40
stageX: 190
stageY: 190
For question 1: After you create the MouseEvent (assigning it a local x,y) you should be able to directly reference e.stageX and set it to what you want prior to dispatching the event. It's just a property of the MouseEvent instance.
For #2, currentTarget is always the thing that is topmost under the mouse, while target is the thing that is dispatching the event -- assuming the event is genuinely being dispatched by mouse interaction. In your case, you can set the target to be whatever object you have dispatching the event, and set the currentTarget arbitrarily. The question really is whether this is the most efficient way to deal with what's under the mouse right now; and the answer is, probably not. You'd be a lot better off using a MOUSE_OVER event to keep tabs on what the mouse is over right now, store that as a variable you can use when you want to call this, and don't try to iterate the whole display chain all the time (because Flash natively does that much faster than you can do it in a loop). If you put a mouseOver on the stage, and just check the currentTarget, you'll be getting whatever the topmost item is under the mouse on every frame where it changes.
You should be aware that (to prevent some obvious nasty scripts), certain actions cannot be triggered by mouse events that are generated dynamically by actionscript. These include opening a file reference and going fullscreen.
I have faced this issue too, gave me a bit of a headache.
In my situation I was creating the event, performing a bit of complex computations, but I couldn't retrieve global coordinates even though I had already set local coordinates.
Actually the solution was quite obvious in my case...
Global coordinates are populated only AFTER the event is dispatched, otherwise how can the event know how to translate local to global?
This is another pitfall, on top of not checking for the object used to dispatch event being an InteractiveObject.
I post this because someone else may face this issue due to both pitfalls. A quick answer easy to read.

Resources