I have an observable with a subscriber that that updates the ui.
someObservable.forEach(function(item) {
updateUI(item);
});
My issue is that after some specific user actions I want to ignore
N items from this stream, and handle it slightly
differently (instead of the default handler).
Say the user does an action which I know will produce four items
in the observable, so I want to postpone updating until the
last of these items has arrived.
I was looking into merging this stream with a "control" stream,
and use groupJoin or something to truncate it, but as far as I
can see this will only work with a time window, and not a count
of items, or even a specific order.
I'd have a variable customActivity somewhere and write the forEach's action roughly like this (my JavaScript knowledge is limited):
customActivity = function(item) {
received++;
handle(item);
if (received == 4) {
customActivity = null;
return true;
}
return false;
}
someObservable.forEach(function(item) {
if (customActivity == null || customActivity(item)) {
updateUI(item);
}
});
You can use take operator to handle only first N values and skip to N of them.
someObservable
.take(4)
.forEach(function(item) {
updateUI(item);
});
someObservable
.skip(4)
.forEach(function(item) {
// ... some code
});
Say the user does an action which I know will produce four items in the observable, so I want to postpone updating until the last of these items has arrived.
In this case bufferWithCount can do the job.
someObservable
.bufferWithCount(4)
.forEach(function(items) {
// will be called for batch of every 4 items
});
Related
Folks I'm using React Native Firebase and I want to achieve as you can see in the attached ss
there's message key and it has threads underneath and each thread have multiple messages.I want to do like I want get last item from every thread and put it into an array.How to achieve.I'm trying to use foreach and not getting my desired result
here is my code
messagesRef.on('value', snapshot => {
let messsagesFB = [];
snapshot.forEach(element => {
messsagesFB = [...messsagesFB, element.val()];
});
});
I know my code will push every element but i want to push only last item from each thread
For your database reference messagesRef, try adding .orderByKey().limitToLast(1) for the query, in which case a loop would not be needed.
messagesRef.orderByKey().limitToLast(1).on('value', snapshot => {
// TODO: Add to array
}
There's also always the catch for the last iteration of a jQuery foreach loop. In your case, you could simply do:
snapshot.forEach(function(element, idx, messagesFB) {
if (idx === messagesFB.length - 1){
messsagesFB.push(element.val());
}
});
I'm trying to implement a version of this intro to RxJS (fiddle here) that instead of picking a random object from a returned API array, it consumes a backthrottled stream of objects from the returned API array.
Here's a portion of the code that produces a controlled Observable from the API response (full fiddle here):
var responseStream = requestStream.flatMap(function (requestUrl) {
return Rx.Observable.fromPromise(fetch(requestUrl));
}).flatMap(function(response) {
return Rx.Observable.fromPromise(response.json());
}).flatMap(function(json) {
return Rx.Observable.from(json);
}).controlled();
I just dump each emitted user in console.log, and use a click event stream to trigger the request() call in the controlled Observable:
responseStream.subscribe(function(user) {
console.log(user);
});
refreshClickStream.subscribe(function (res) {
responseStream.request(1);
});
There's about 50 user objects returned from the GitHub API, and I'd like to backthrottle-consume them one per click (as seen above). However, after I'm fresh out of user objects I'd like to send in another call to requestStream to fetch another API call, replenish the responseStream and continue providing user objects to console.log upon each click. What would be the RxJS-friendly way to do so?
I'd do it similarly to the article example with combineLatest() although I wonder if there's an easier way than mine.
I'm making request for only 3 items. Working with 3 items is hardcoded so you'll want to modify this. I was thinking about making it universal but that would require using Subject and made it much more complicated so I stayed with this simple example.
Also, I'm using concatMap() to trigger fetching more data. However, just clicking the link triggers just the combineLatest() which emits another item from the array.
See live demo: https://jsfiddle.net/h3bwwjaz/12/
var refreshButton = document.querySelector('#main');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click')
.startWith(0)
.scan(function(acc, val, index) {
return index;
});
var usersStream = refreshClickStream
.filter(function(index) {
return index % 3 === 0;
})
.concatMap(function() {
var randomOffset = Math.floor(Math.random() * 500);
var url = 'https://api.github.com/users?since=' + randomOffset + '&per_page=3';
return Rx.Observable.fromPromise(fetch(url))
.flatMap(function(response) {
return Rx.Observable.fromPromise(response.json());
});
})
.combineLatest(refreshClickStream, function(responseArray, index) {
return responseArray[index % 3];
})
.distinct();
usersStream.subscribe(function(user) {
console.log(user);
});
I use refreshClickStream twice:
to emit next item in the array in combineLatest()
to check whether this is the end of the array and we need to make another request (that's the filter() operator).
At the end distinct() is required because when you click index % 3 === 0 time triggers in fact two emission. First is the one from downloading the data and the second one is directly in combineLatest() that we want to ignore because we don't want to iterate the same data again. Thanks to distinct() it's ignored and only the new values is passed.
I was trying to figure out a method without using distinct() but I couldn't find any.
Odd question I guess...
I'm building my DOM in memory and make heavy use of promises. Say I have this inside a for... loop:
target = document.createDocumentFragment();
promises = [], pass, skip, store;
for (i = 0; i < foo; i += 1) {
element = foo[i];
// set promise
promises[i] = app.setContent(element, {}, update)
.then(function(response) {
// HELP!
if (pass) {
target.appendChild(store)
store = undefined;
skip = undefined;
pass = undefined;
}
if (response.tagName !== undefined) {
pass = true;
}
if (skip === undefined && response.tagName === undefined) {
store = response;
skip = true;
} else {
target.appendChild(response);
}
});
RSVP.all(promises).then(continue...
The loop above runs 3x returning two div tags and a documentFragment. Without promises, no problem, my structure is
<div toolbar>
<fragment = form>
<div toolbar>
Problem is, when I converted to async I have no more say in the order items get appended forcing me to stupid things as above, where I'm finding my fragment, store it in store, set a skip parameter, append my first div, which sets pass, which will allow my stored fragment to be appended when the next promises "comes in"... what a waste of code...
Question:
Is there any way to properly set the order of items in a generic way when all items are being returned async? If not, how do I move items? Since I'm working in memory, I can select using querySelector/qsAll but I don't have a lot of other options? Any ideas?
(And please don't suggest to put in the DOM and then shuffle :-))
Thanks!
I'll explain in words ...
On making each async request, also create a container, in the right place, for the requested data to go when it eventually arrives. This container will typically be a <div> or a <span>.
Keep a reference to the container (typically in a closure) such that the async response handler knows which container corresponds to which data.
I need to keep track of a counter of a collection with a huge number of documents that's constantly being updated. (Think a giant list of logs). What I don't want to do is to have the server send me a list of 250k documents. I just want to see a counter rising.
I found a very similar question here, and I've also looked into the .observeChanges() in the docs but once again, it seems that .observe() as well as .observeChanges() actually return the whole set before tracking what's been added, changed or deleted.
In the above example, the "added" function will fire once per every document returned to increment a counter.
This is unacceptable with a large set - I only want to keep track of a change in the count as I understand .count() bypasses the fetching of the entire set of documents. The former example involves counting only documents related to a room, which isn't something I want (or was able to reproduce and get working, for that matter)
I've gotta be missing something simple, I've been stumped for hours.
Would really appreciate any feedback.
You could accomplish this with the meteor-streams smart package by Arunoda. It lets you do pub/sub without needing the database, so one thing you could send over is a reactive number, for instance.
Alternatively, and this is slightly more hacky but useful if you've got a number of things you need to count or something similar, you could have a separate "Statistics" collection (name it whatever) with a document containing that count.
There is an example in the documentation about this use case. I've modified it to your particular question:
// server: publish the current size of a collection
Meteor.publish("nbLogs", function () {
var self = this;
var count = 0;
var initializing = true;
var handle = Messages.find({}).observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", roomId, {nbLogs: count});
},
removed: function (id) {
count--;
self.changed("counts", roomId, {nbLogs: count});
}
// don't care about moved or changed
});
// Observe only returns after the initial added callbacks have
// run. Now return an initial value and mark the subscription
// as ready.
initializing = false;
self.added("counts", roomId, {nbLogs: count});
self.ready();
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Meteor.Collection("counts");
// client: subscribe to the count for the current room
Meteor.subscribe("nbLogs");
// client: use the new collection
Deps.autorun(function() {
console.log("nbLogs: " + Counts.findOne().nbLogs);
});
There might be some higher level ways to do this in the future.
NOTICE: THIS IS SOLVED, I WILL PUBLISH THE SOLUTION HERE ASAP.
Hey all,
Ok... I have a simple dojo page with the bare essentials. Three UL's with some LI's in them. The idea si to allow drag-n-drop among them but if any UL goes empty due to the last item being dragged out, I will put up a message to the user to gie them some instructions.
In order to do that, I wanted to extend the dojo.dnd.Source dijit and add some intelligence. It seemed easy enough. To keep things simple (I am loading Dojo from a CDN) I am simply declating my extension as opposed to doing full on module load. The declaration function is here...
function declare_mockupSmartDndUl(){
dojo.require("dojo.dnd.Source");
dojo.provide("mockup.SmartDndUl");
dojo.declare("mockup.SmartDndUl", dojo.dnd.Source, {
markupFactory: function(params, node){
//params._skipStartup = true;
return new mockup.SmartDndUl(node, params);
},
onDndDrop: function(source, nodes, copy){
console.debug('onDndDrop!');
if(this == source){
// reordering items
console.debug('moving items from us');
// DO SOMETHING HERE
}else{
// moving items to us
console.debug('moving items to us');
// DO SOMETHING HERE
}
console.debug('this = ' + this );
console.debug('source = ' + source );
console.debug('nodes = ' + nodes);
console.debug('copy = ' + copy);
return dojo.dnd.Source.prototype.onDndDrop.call(this, source, nodes, copy);
}
});
}
I have a init function to use this to decorate the lists...
dojo.addOnLoad(function(){
declare_mockupSmartDndUl();
if(dojo.byId('list1')){
//new mockup.SmartDndUl(dojo.byId('list1'));
new dojo.dnd.Source(dojo.byId('list1'));
}
if(dojo.byId('list2')){
new mockup.SmartDndUl(dojo.byId('list2'));
//new dojo.dnd.Source(dojo.byId('list2'));
}
if(dojo.byId('list3')){
new mockup.SmartDndUl(dojo.byId('list3'));
//new dojo.dnd.Source(dojo.byId('list3'));
}
});
It is fine as far as it goes, you will notice I left "list1" as a standard dojo dnd source for testing.
The problem is this - list1 will happily accept items from lists 2 & 3 who will move or copy as apprriate. However lists 2 & 3 refuce to accept items from list1. It is as if the DND operation is being cancelled, but the debugger does show the dojo.dnd.Source.prototype.onDndDrop.call happening, and the paramaters do look ok to me.
Now, the documentation here is really weak, so the example I took some of this from may be way out of date (I am using 1.4).
Can anyone fill me in on what might be the issue with my extension dijit?
Thanks!
If you use Dojo XD loader (used with CDNs), all dojo.require() are asynchronous. Yet declare_mockupSmartDndUl() assumes that as soon as it requires dojo.dnd.Source it is available. Generally it is not guaranteed.
Another nitpicking: dojo.dnd.Source is not a widget/dijit, while it is scriptable and can be used with the Dojo Markup, it doesn't implement any Dijit's interfaces.
Now the problem — the method you are overriding has following definition in 1.4:
onDndDrop: function(source, nodes, copy, target){
// summary:
// topic event processor for /dnd/drop, called to finish the DnD operation
// source: Object
// the source which provides items
// nodes: Array
// the list of transferred items
// copy: Boolean
// copy items, if true, move items otherwise
// target: Object
// the target which accepts items
if(this == target){
// this one is for us => move nodes!
this.onDrop(source, nodes, copy);
}
this.onDndCancel();
},
Notice that it has 4 arguments, not 3. As you can see if you do not pass the 4th argument, onDrop is never going to be called by the parent method.
Fix these two problems and most probably you'll get what you want.
In the end, I hit the Dojo IRC (great folks!) and we ended up (so far) with this...
function declare_mockupSmartDndUl(){
dojo.require("dojo.dnd.Source");
dojo.provide("mockup.SmartDndUl");
dojo.declare("mockup.SmartDndUl", dojo.dnd.Source, {
markupFactory: function(params, node){
//params._skipStartup = true;
return new mockup.SmartDndUl(node, params);
},
onDropExternal: function(source, nodes, copy){
console.debug('onDropExternal called...');
// dojo.destroy(this.getAllNodes().query(".dndInstructions"));
this.inherited(arguments);
var x = source.getAllNodes().length;
if( x == 0 ){
newnode = document.createElement('li');
newnode.innerHTML = "You can drag stuff here!";
dojo.addClass(newnode,"dndInstructions");
source.node.appendChild(newnode);
}
return true;
// return dojo.dnd.Source.prototype.onDropExternal.call(this, source, nodes, copy);
}
});
}
And you can see where I am heading, I put in a message when the source is empty (client specs, ug!) and I need to find a way to kill it when something gets dragged in (since it is not, by definition, empty any more ona incomming drag!). That part isnt workign so well.
Anyway, the magic was not to use the onDnd_____ functions, but the higher level one and then call this.inherited(arguments) to fire off the built in functionality.
Thanks!
dojo.require("dojo.dnd.Source");
dojo.provide("mockup.SmartDndUl");
dojo.declare("mockup.SmartDndUl", dojo.dnd.Source, {
Dojo require statement and declare statement are next to next. I think that will cause dependencies problem.
the dojo require statement should go outside onload block and the declare statement should be in onload block.