In the following code:
http://jsfiddle.net/staltz/4gGgs/27/
var clickStream = Rx.Observable.fromEvent(button, 'click');
var multiClickStream = clickStream
.buffer(function() { return clickStream.throttle(250); })
.map(function(list) { return list.length; })
.filter(function(x) { return x > 1; });
// Same as above, but detects single clicks
var singleClickStream = clickStream
.buffer(function() { return clickStream.throttle(250); })
.map(function(list) { return list.length; })
.filter(function(x) { return x === 1; });
// Listen to both streams and render the text label accordingly
singleClickStream.subscribe(function (event) {
document.querySelector('h2').textContent = 'click';
});
multiClickStream.subscribe(function (numclicks) {
document.querySelector('h2').textContent = ''+numclicks+'x click';
});
Rx.Observable.merge(singleClickStream, multiClickStream)
.throttle(1000)
.subscribe(function (suggestion) {
document.querySelector('h2').textContent = '';
});
How many times clickStream sequence will be iterated after merge?
I mean, will it look like this:
case 1
for(numclicks : clickStream.length){
if (numclicks === 1){
document.querySelector('h2').textContent = 'click';
}
};
for(numclicks : clickStream.length){
if (numclicks > 1){
document.querySelector('h2').textContent = ''+numclicks+'x click';
}
};
Or it will be internally, really merged to something like this (pseudocode):
case 2
for(numclicks: clickStream.length){
if (numclicks === 1){
document.querySelector('h2').textContent = 'click';
}else if(numclicks > 1){
document.querySelector('h2').textContent = ''+numclicks+'x click';
}
}
I personally think, that merge just sequentially apply stream to its arguments (case 1).
P.S. I hope there is some standart for things like this. But if no - I particularly interested in RxCpp and Sodium implementation.
I took js example, as more interactive.
fromEvent returns a hot source and so all subscribers share the same iteration of the for loop.
Ignoring the throttle calls, the result is similar to:
for(numclicks: clickStream.length){
// first subscription
if (numclicks === 1){
document.querySelector('h2').textContent = 'click';
}
// second subscription
if(numclicks > 1){
document.querySelector('h2').textContent = ''+numclicks+'x click';
}
// merged subscription
if (numclicks === 0) {
document.querySelector('h2').textContent = '';
}
}
The throttle calls mean that the body of the sole click stream for loop is actually just pushing click events into two buffers and reseting the timer in each of the three throttle operators. h2 is set when one of the three throttle timers fires. since the timers are not shared it is like a separate for loop per throttle timer with each loop setting h2 to only one of the three possible values:
This behavior is similar in all the Rx family.
Regarding rxcpp in particular:
rxcpp is missing the buffer overload that allows a observable to trigger a transition to a new buffer.
rxcpp does not have throttle implemented yet.
rxcpp is not thread-safe by default (pay-for-play) so if the throttle timers used introduce threads, then coordinations must be used to explicitly add thread-safety.
Related
I use combineLatest for join of two streams with two types of tasks. Processing two types of tasks should be interleaved. Is possible to determine which stream emits last value of pair?
I use solution with timestamp, but it is not correct. Each subject contain default value.
List<Flowable<? extends Timed<? extends Task>>> sources = new ArrayList<>();
Flowable<Timed<TaskModification>> modificationSource = mTaskModificationSubject
.onBackpressureDrop()
.observeOn(Schedulers.io(), false, 1)
.timestamp();
Flowable<Timed<TaskSynchronization>> synchronizationSource = mTaskSynchronizationSubject
.onBackpressureDrop()
.observeOn(Schedulers.io(), false, 1)
.flatMap(TaskSynchronizationWrapper::getSources)
.timestamp();
sources.add(0, modificationSource);
sources.add(1, synchronizationSource);
return Flowable
.combineLatest(sources, array -> {
Timed<TaskModification> taskModification = (Timed<TaskModification>) array[0];
Timed<TaskSynchronization> taskSynchronization = (Timed<TaskSynchronization>) array[1];
return (taskModification.time() > taskSynchronization.time())
? taskModification.value()
: taskSynchronization.value();
}, 1)
.observeOn(Schedulers.io(), false, 1)
.flatMapSingle(
Task::getSource
)
.ignoreElements();
When modification task is emitted than should have priority before synchronization tasks.
Without implementing a custom operator, you could introduce queues, merge the signals, then pick items from the priority queue first:
Flowable<X> prioritySource = ...
Flowable<X> source = ...
Flowable<X> output = Flowable.defer(() -> {
Queue<X> priorityQueue = new ConcurrentLinkedQueue<>();
Queue<X> queue = new ConcurrentLinkedQueue<>();
return Flowable.merge(
prioritySource.map(v -> {
priorityQueue.offer(v);
return 1;
}),
source.map(v -> {
queue.offer(v);
return 1;
})
)
.map(v -> {
if (!priorityQueue.isEmpty()) {
return priorityQueue.poll();
}
return queue.poll();
});
});
I love Google Forms I can play with them for hours. I have spent days trying to solve this one, searching for an answer. It is very much over my head. I have seen similar questions but none that seemed to have helped me get to an answer. We have a café where I work and I created a pre-order form on Google Forms. That was the easy part. The Café can only accept pre-orders up to 10:30am. I want the form to open at 7am and close at 10:30am everyday to stop people pre ordering when the café isn't able to deal with their order. I used the very helpful tutorial from http://labnol.org/?p=20707 to start me off I have added and messed it up and managed to get back to the below which is currently how it looks. It doesn't work and I can't get my head around it. At one point I managed to turn it off but I couldn't turn it back on!! I'm finding it very frustrating and any help in solving this would be amazing. To me it seems very simple as it just needs to turn on and off at a certain time every day. I don't know! Please help me someone?
FORM_OPEN_DATE = "7:00";
FORM_CLOSE_DATE = "10:30";
RESPONSE_COUNT = "";
/* Initialize the form, setup time based triggers */
function Initialize() {
deleteTriggers_();
if ((FORM_OPEN_DATE !== "7:00") &&
((new Date()).getTime("7:00") < parseDate_(FORM_OPEN_DATE).getTime ("7:00"))) {
closeForm("10:30");
ScriptApp.newTrigger("openForm")
.timeBased("7:00")
.at(parseDate_(FORM_OPEN_DATE))
.create(); }
if (FORM_CLOSE_DATE !== "10:30") {
ScriptApp.newTrigger("closeForm")
.timeBased("10:30")
.at(parseDate_(FORM_CLOSE_DATE))
.create(); }
if (RESPONSE_COUNT !== "") {
ScriptApp.newTrigger("checkLimit")
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create(); } }
/* Delete all existing Script Triggers */
function deleteTriggers_() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
/* Allow Google Form to Accept Responses */
function openForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(true);
informUser_("Your Google Form is now accepting responses");
}
/* Close the Google Form, Stop Accepting Reponses */
function closeForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
deleteTriggers_();
informUser_("Your Google Form is no longer accepting responses");
}
/* If Total # of Form Responses >= Limit, Close Form */
function checkLimit() {
if (FormApp.getActiveForm().getResponses().length >= RESPONSE_COUNT ) {
closeForm();
}
}
/* Parse the Date for creating Time-Based Triggers */
function parseDate_(d) {
return new Date(d.substr(0,4), d.substr(5,2)-1,
d.substr(8,2), d.substr(11,2), d.substr(14,2));
}
I don't think you can use .timebased('7:00'); And it is good to check that you don't have a trigger before you try creating a new one so I like to do this. You can only specify that you want a trigger at a certain hour like say 7. The trigger will be randomly selected somewhere between 7 and 8. So you really can't pick 10:30 either. It has to be either 10 or 11. If you want more precision you may have to trigger your daily triggers early and then count some 5 minute triggers to get you closer to the mark. You'll have to wait to see where the daily triggers are placed in the hour first. Once they're set they don't change.
I've actually played around with the daily timers in a log by creating new ones until I get one that close enough to my desired time and then I turn the others off and keep that one. You have to be patient. As long as you id the trigger by the function name in the log you can change the function and keep the timer going.
Oh and I generally created the log file with drive notepad and then open it up whenever I want to view the log.
function formsOnOff()
{
if(!isTrigger('openForm'))
{
ScriptApp.newTrigger('openForm').timeBased().atHour(7).create()
}
if(!isTrigger('closeForm'))
{
ScriptApp.newTrigger('closeForm').timeBased().atHour(11)
}
}
function isTrigger(funcName)
{
var r=false;
if(funcName)
{
var allTriggers=ScriptApp.getProjectTriggers();
var allHandlers=[];
for(var i=0;i<allTriggers.length;i++)
{
allHandlers.push(allTriggers[i].getHandlerFunction());
}
if(allHandlers.indexOf(funcName)>-1)
{
r=true;
}
}
return r;
}
I sometimes run a log entry on my timers so that I can figure out exactly when they're happening.
function logEntry(entry,file)
{
var file = (typeof(file) != 'undefined')?file:'eventlog.txt';
var entry = (typeof(entry) != 'undefined')?entry:'No entry string provided.';
if(entry)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "yyyy-MM-dd' 'hh:mm:ss a");
var s = ts + ' - ' + entry + '\n';
myUtilities.saveFile(s, file, true);//this is part of a library that I created. But any save file function will do as long as your appending.
}
}
This is my utilities save file function. You have to provide defaultfilename and datafolderid.
function saveFile(datstr,filename,append)
{
var append = (typeof(append) !== 'undefined')? append : false;
var filename = (typeof(filename) !== 'undefined')? filename : DefaultFileName;
var datstr = (typeof(datstr) !== 'undefined')? datstr : '';
var folderID = (typeof(folderID) !== 'undefined')? folderID : DataFolderID;
var fldr = DriveApp.getFolderById(folderID);
var file = fldr.getFilesByName(filename);
var targetFound = false;
while(file.hasNext())
{
var fi = file.next();
var target = fi.getName();
if(target == filename)
{
if(append)
{
datstr = fi.getBlob().getDataAsString() + datstr;
}
targetFound = true;
fi.setContent(datstr);
}
}
if(!targetFound)
{
var create = fldr.createFile(filename, datstr);
if(create)
{
targetFound = true;
}
}
return targetFound;
}
I am trying to find a specific element from my page using ExtJS 4 so I can do modifications on it.
I know its id so it should not be a problem BUT
-I tried Ext.getCmp('theId') and it just return me undefined
-I tried to use down('theId') method by passing through the view and I still get a nullresponse.
As I know the id of the element I tried again the two methods by setting manually the id and it didn't work neither.
Do these two methods not function?
How should I do?
Here is the concerned part of the code :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
// Add row stripping on leaf nodes when a node is expanded
//
// Adding the same feature to the whole tree instead of leaf nodes
// would not be much more complicated but it would require iterating
// the whole tree starting with the root node to build a list of
// all visible nodes. The same function would need to be called
// on expand, collapse, append, insert, remove and load events.
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ((index % 2) == 0) {
// even node
currentChild.data.cls.replace('tree-odd-node', '')
currentChild.data.cls = 'tree-even-node';
} else {
// odd node
currentChild.data.cls.replace('tree-even-node', '')
currentChild.data.cls = 'tree-odd-node';
}
// Update CSS classes
currentChild.triggerUIUpdate();
console.log(nodeId);
console.log(ownertree.view.body);
console.log(Ext.getCmp(nodeId));
console.log(Ext.getCmp('treeview-1016-record-02001001'));
console.log(ownertree.view.body.down(nodeId));
console.log(ownertree.view.body.down('treeview-1016-record-02001001'));
}
});
}
}
You can see my console.log at the end.
Here is what they give me on the javascript console (in the right order):
treeview-1016-record-02001001
The precise id I am looking for. And I also try manually in case...
h {dom: table#treeview-1016-table.x-treeview-1016-table x-grid-table, el: h, id: "treeview-1016gridBody", $cache: Object, lastBox: Object…}
I checked every configs of this item and its dom and it is exactly the part of the dom I am looking for, which is the view containing my tree. The BIG parent
And then:
undefined
undefined
null
null
Here is the item I want to access:
<tr role="row" id="treeview-1016-record-02001001" ...>
And I checked there is no id duplication anywhere...
I asked someone else who told me these methods do not work. The problem is I need to access this item to modify its cls.
I would appreciate any idea.
You are looking for Ext.get(id). Ext.getCmp(id) is used for Ext.Components, and Ext.get(id) is used for Ext.dom.Elements. See the docs here: http://docs.sencha.com/extjs/4.2.1/#!/api/Ext-method-get
Ok so finally I used the afteritemexpand listener. With the ids I get the elements I am looking for with your Ext.get(id) method kevhender :).
The reason is that the dom elements where not completely loaded when I used my load listener (it was just the store) so the Ext.get(id) method couldn't get the the element correctly. I first used afterlayout listener, that was correct but too often called and the access to the id was not so easy.
So, here is how I did finally :
listeners: {
load: function(node, records, successful, eOpts) {
var ownertree = records.store.ownerTree;
var boundView = ownertree.dockedItems.items[1].view.id;
var generalId = boundView+'-record-';
if (!node.tree.root.data.leaf) {
// Process each child node
node.tree.root.cascadeBy(function(currentChild) {
// Process only leaf
if (currentChild.data.leaf) {
var nodeId = ""+generalId+currentChild.internalId;
var index = currentChild.data.index;
if ( (index % 2) == 0 && ids.indexOf(nodeId) == -1 ) {
ids[indiceIds] = nodeId;
indiceIds++;
}
console.log(ids);
}
});
}
},
afteritemexpand: function( node, index, item, eOpts ){
/* This commented section below could replace the load but the load is done during store loading while afteritemexpand is done after expanding an item.
So, load listener makes saving time AND makes loading time constant. That is not the case if we just consider the commented section below because
the more you expand nodes, the more items it will have to get and so loading time is more and more important
*/
// var domLeaf = Ext.get(item.id).next();
// for ( var int = 0; int < node.childNodes.length; int++) {
// if (node.childNodes[int].data.leaf && (int % 2) == 0) {
// if (ids.indexOf(domLeaf.id) == -1) {
// ids[indiceIds] = domLeaf.id;
// indiceIds++;
// }
// }
// domLeaf = domLeaf.next();
// }
for ( var int = 0; int < ids.length; int++) {
domLeaf = Ext.get(ids[int]);
if (domLeaf != null) {
for ( var int2 = 0; int2 < domLeaf.dom.children.length; int2++) {
if (domLeaf.dom.children[int2].className.search('tree-even-node') == -1){
domLeaf.dom.children[int2].className += ' tree-even-node';
}
}
}
}
},
With ids an Array of the ids I need to set the class.
Thank you for the method.
first time long time here.
I just started programming in javascript recently, I'm running into a question of design.
I have some working code that:
1. Waits for specific input from the serial port,
2. When input is found it moves to the next function.
3. The next function sends a command(s) over the serial port and then waits for input again.
Now I have 9 functions defined as stepone() steptwo() etc.... There has to be a better way to do this. Each function is the same except with different variables for input and output desired.
However, I do not want the program to skip steps. It needs to wait for the correct serial input before sending the next command.
I've tried using callback functions referencing each other, it just seems...wrong?
Also, it doesn't work. It doesn't wait for the right input before sending commands.
var waitforinput = function(input, regex, callback)
{
if (regex.search != -1)
callback();
};
var sendcommand = function(command,callback)
{
port.writeline(command);
if (callback)
callback();
};
var connect = function()
{
var int = setInterval(function()
{
waitforinput(input, "Please choose:", function()
{
sendcommand("1", function()
{
waitforinput(input, "You choosed", function()
{
sendcommand("saveenv 1");
});
});
});
},50);
};
I ended up using switch() with cases and keeping track of a variable called step:
step = 1;
switch(step)
{
case 1:
if (inputbuffer.search('Please choose') !== -1)
{
if (!waitdisplaystarted)
{
waitdisplaystarted = true;
waitint = setInterval(showwait,1000);
}
window.$("#instructions").hide();
window.$("#status").html("Step 1: Choosing boot option.");
SELF.sendserialcommand("1");
step = 2;
}
break;
case 2:
if (inputbuffer.search('You choosed 1') !== -1)
{
SELF.sendserialcommand('setenv bootargs "board=ALFA console=ttyATH0,115200 rootfstype=squashfs,jffs2 noinitrd"\r');
setTimeout(function(){SELF.sendserialcommand('saveenv\r');}, 50);
window.$("#status").html("Step 2: Transferring new kernel.");
setTimeout(function(){SELF.sendserialcommand('tftp 0x80600000 kernel.bin\r');}, 2000);
step = 3;
}
break;
case 3:
if (inputbuffer.search('Bytes transferred = ' + 878938) !== -1)
{
window.$("#status").html("Step 3: Erasing old kernel.");
SELF.sendserialcommand('erase 0x9f650000 +0x190000\r');
step = 'finished';
}
}
I want to stop stream A for exactly one notification whenever stream B fires. Both streams will stay online and won't ever complete.
A: o--o--o--o--o--o--o--o--o
B: --o-----o--------o-------
R: o-----o-----o--o-----o--o
or
A: o--o--o--o--o--o--o--o--o
B: -oo----oo-------oo-------
R: o-----o-----o--o-----o--o
Here's a version of my SkipWhen operator I did for a similar question (the difference is that, in the original, multiple "B's" would skip multiple "A's"):
public static IObservable<TSource> SkipWhen<TSource, TOther>(this IObservable<TSource> source,
IObservable<TOther> other)
{
return Observable.Create<TSource>(observer =>
{
object lockObject = new object();
bool shouldSkip = false;
var otherSubscription = new MutableDisposable();
var sourceSubscription = new MutableDisposable();
otherSubscription.Disposable = other.Subscribe(
x => { lock(lockObject) { shouldSkip = true; } });
sourceSubscription.Disposable = source.Where(_ =>
{
lock(lockObject)
{
if (shouldSkip)
{
shouldSkip = false;
return false;
}
else
{
return true;
}
}
}).Subscribe(observer);
return new CompositeDisposable(
sourceSubscription, otherSubscription);
});
}
If the current implementation becomes a bottleneck, consider changing the lock implementation to use a ReaderWriterLockSlim.
This solution will work when the observable is hot (and without refCount):
streamA
.takeUntil(streamB)
.skip(1)
.repeat()
.merge(streamA.take(1))
.subscribe(console.log);
.takeUntil(streamB): make stream A complete upon stream B producing a value.
.skip(1): make stream A skip one value upon starting (or as a result of .repeat()).
.repeat(): make stream A repeat (reconnect) indefinitely.
.merge(streamA.take(1)): offset the effect of .skip(1) at the beginning of the stream.
Example of making A stream skip every 5 seconds:
var streamA,
streamB;
streamA = Rx.Observable
.interval(1000)
.map(function (x) {
return 'A:' + x;
}).publish();
streamB = Rx.Observable
.interval(5000);
streamA
.takeUntil(streamB)
.skip(1)
.repeat()
.merge(streamA.take(1))
.subscribe(console.log);
streamA.connect();
You can also use this sandbox http://jsbin.com/gijorid/4/edit?js,console to execute BACTION() in the console log at the time of running the code to manually push a value to streamB (which is helpful for analysing the code).