rxdart BehaviorSubject unpredictable behavior - dart
I start using BehaviorSubject class
because I need broadcast stream and
A special StreamController that captures the latest item that has been added to the controller, and emits that as the first item to any new listener.
The test code below:
import 'package:rxdart/rxdart.dart';
void main() {
BehaviorSubject<int> subject = BehaviorSubject<int>.seeded(0);
subject.stream.listen((a) => print("listener 1 : $a"));
subject.add(1);
subject.add(2);
subject.stream.listen((a) => print("listener 2 : $a"));
subject.add(3);
subject.stream.listen((a) => print("listener 3 : $a"));
subject.add(4);
subject.close();
}
The result:
listener 1 : 0
listener 1 : 1
listener 2 : 2
listener 2 : 3
listener 3 : 3
listener 3 : 4
listener 1 : 2
listener 2 : 4
listener 1 : 3
listener 1 : 4
Exited
Although I need the behavior of the instant listens of the stored value of the streams, the result order is so unpredictable to me on what I code.
I need more specifications for the BehaviorSubject class but cannot find out why the stream behaves this way.
I'd like to know how this API works.
A BehaviorSubject is not really a broadcast stream. It's a multi-cast stream (multiple listeners), but they don't all get the same events at the same time like a broadcast stream would. (Since they get the latest event at time of listening).
The most likely reason for the displayed behavior (I'm guessing, I haven't checked the BehaviorSubject implementation, I just happen to understand Dart streams very well) is that event delivery is asynchronous and events get enqueued.
If you add a print("done"); after the subject.close();, it will print before any of the listener x: y lines. That's because all the code does until then is to schedule microtasks to later deliver events.
It seems the BehaviorSubject schedules one microtask to deliver the first event to each listener (probably calling onData directly), then one further microtask the next time a value is added to the subject, which is where it gets added to the listener's event queue. After that, and adding more events to the queue won't trigger more microtasks.
Instead the queue elements will be delivered one by one, requesting a new microtask after each one.
That will give the following behavior (notation: M1:L2[3] means microtask M1 is scheduled to deliver event 3 to listener 2):
+─────────────+───────────────────────────────────────────────────────────────────────────────+──────────────────+
| action | microtasks | prints |
+─────────────+───────────────────────────────────────────────────────────────────────────────+──────────────────+
| `listen` 1 | M1:L1[0] | |
| `add(1)` | M1:L1[0],M2:L1[1] | |
| `add(2)` | M1:L1[0],M2:L1[1, 2] | |
| `listen` 2 | M1:L1[0],M2:L1[1, 2],M3:L2[2] | |
| `add(3)` | M1:L1[0],M2:L1[1,2,3],M3:L2[2],M4:L2[3] | |
| `listen` 3 | M1:L1[0],M2:L1[1,2,3],M3:L2[2],M4:L2[3],M5:L3[3] | |
| `add(4)` | M1:L1[0],M2:L1[1,2,3,4],M3:L2[2],M4:L2[3,4],M5:L3[3],M6:L3[4] | |
| `close` | M1:L1[0],M2:L1[1,2,3,4,DONE],M3:L2[2],M4:L2[3,4,DONE],M5:L3[3],M6:L3[4,DONE] | |
| microtask | M2:L1[1,2,3,4,DONE],M3:L2[2],M4:L2[3,4,DONE],M5:L3[3],M6:L3[4,DONE] | `listener 1: 0` |
| microtask | M3:L2[2],M4:L2[3,4,DONE],M5:L3[3],M6:L3[4,DONE],M7:L1[2,3,4,DONE] | `listener 1: 1` |
| microtask | M4:L2[3,4,DONE],M5:L3[3],M6:L3[4,DONE],M7:L1[2,3,4,DONE] | `listener 2: 2` |
| microtask | M5:L3[3],M6:L3[4,DONE],M7:L1[2,3,4,DONE],M8:L2[4,DONE], | `listener 2: 3` |
| microtask | M6:L3[4,DONE],M7:L1[2,3,4,DONE],M8:L2[4,DONE], | `listener 3: 3` |
| microtask | M7:L1[2,3,4,DONE],M8:L2[4,DONE],M9:L3[DONE] | `listener 3: 4` |
| microtask | M8:L2[4,DONE],M9:L3[DONE],M10:L1[3,4,DONE] | `listener 1: 2` |
| microtask | M9:L3[DONE],M10:L1[3,4,DONE],M11:L2[DONE] | `listener 2: 4` |
| microtask | M10:L1[3,4,DONE],M11:L2[DONE] | |
| microtask | M11:L2[DONE],M12:L1[4,DONE] | `listener 1: 3 |
| microtask | M12:L1[4,DONE] | |
| microtask | M13:L1[DONE] | `listener 1: 4 |
| microtask | | |
+─────────────+───────────────────────────────────────────────────────────────────────────────+──────────────────+
You can see this matches your result. So, things work the way they do because of the way they're implemented, and because it's relying on an event enqueuing which puts itself at the back of the microtask queue after delivering each event, so the longest queue gets done last.
You can explore the behavior yourself by printing when microtasks are scheduled and run:
import 'dart:async';
import 'package:rxdart/rxdart.dart';
void main() {
int ctr = 0;
runZoned(() {
BehaviorSubject<int> subject = BehaviorSubject<int>.seeded(0);
print("listen 1");
subject.stream.listen((a) => print("listener 1 : $a"), onDone: () {
print('listener 1 done');
});
print("add 1");
subject.add(1);
print("add 2");
subject.add(2);
print("listen 2");
subject.stream.listen((a) => print("listener 2 : $a"), onDone: () {
print('listener 2 done');
});
print("add 3");
subject.add(3);
print("listen 3");
subject.stream.listen((a) => print("listener 3 : $a"), onDone: () {
print('listener 3 done');
});
print("add 4");
subject.add(4);
print("close");
subject.close();
print("done");
}, zoneSpecification: ZoneSpecification(scheduleMicrotask: (s, p, z, f) {
var id = ++ctr;
print("Schedule microtask $id");
p.scheduleMicrotask(z, () {
print("Run microtask $id");
f();
});
}));
}
which prints:
listen 1
Schedule microtask 1
add 1
Schedule microtask 2
add 2
listen 2
Schedule microtask 3
add 3
Schedule microtask 4
listen 3
Schedule microtask 5
add 4
Schedule microtask 6
close
done
Run microtask 1
listener 1 : 0
Run microtask 2
listener 1 : 1
Schedule microtask 7
Run microtask 3
listener 2 : 2
Run microtask 4
listener 2 : 3
Schedule microtask 8
Run microtask 5
listener 3 : 3
Run microtask 6
listener 3 : 4
Schedule microtask 9
Run microtask 7
listener 1 : 2
Schedule microtask 10
Run microtask 8
listener 2 : 4
Schedule microtask 11
Run microtask 9
Run microtask 10
listener 1 : 3
Schedule microtask 12
Run microtask 11
listener 3 done
listener 2 done
Run microtask 12
listener 1 : 4
Schedule microtask 13
Run microtask 13
Schedule microtask 14
Run microtask 14
listener 1 done
(The "done" are delayed, probably because they need to wait for another future.)
Related
How to construct a line graph from a time series table in Neo4j using Cypher?
How to construct a line graph from a time series table in Neo4j using Cypher? For example: Here is a table id | event | timestamp ---------------------- 1 | event1 | 10:00 AM 1 | event2 | 10:15 AM 1 | event3 | 10:30 AM . | . | . . | . | . . | . | . 1 | event100 | 8:00 PM 2 | event25 | 10:10 AM 2 | event30 | 10:20 AM 2 | event150 | 11:20 AM . | . | . . | . | . . | . | . so I want to group the events by id and order them according to timestamp and construct the graph like this for each group event1 -> event2 -> ... -> event100 and event25 -> event30 -> event150 -> ...
If you're happy adding the APOC plugin to your database, what you're describing is a linked list that can be created using apoc.nodes.link. Some example data: MERGE (e1: Event { id: 1, event: 'Event1', timestamp: localdatetime('20200323T10:00:00') }) MERGE (e2: Event { id: 1, event: 'Event3', timestamp: localdatetime('20200323T10:20:00') }) MERGE (e3: Event { id: 1, event: 'Event2', timestamp: localdatetime('20200323T10:05:00') }) MERGE (e4: Event { id: 2, event: 'Event5', timestamp: localdatetime('20200323T10:08:00') }) MERGE (e5: Event { id: 2, event: 'Event4', timestamp: localdatetime('20200323T10:00:00') }) Then we can group, sort and create the linked lists: MATCH (e: Event) WITH e ORDER BY e.timestamp WITH e.id as id, collect(e) as nodes CALL apoc.nodes.link(nodes, 'PRECEDES') RETURN nodes
Maximum of column 1 where value of column 2 matches some condition
Let's say I have the following in a table : A | B | desired_output ---------------------------- 1 | 10 | 1 | 0 2 | 20 | 7 | 0 3 | 30 | 3 | 0 4 | 20 | 2 | 0 5 | 30 | 5 | 1 I'd like to find a formula for each of the cells in the desired_output column which looks at the max of B1:B5 but only for rows for which A = max(A1:A5) If that's not clear, I'll try to put it another way : for all the rows in A1:A5 that are equal to max(A1:A5) // so that's rows 3 and 5 find the one which has the max value on B // so between B3 and B5, that's B5 output 1 for this one, 0 for the other I'd say there would be a where somewhere if such a function existed, something like = if(B=(max(B1:B5) where A = max(A1:A5)), 1, 0) but I can't find how to do it... I can do it in two columns with a trick : A | B | C | D ---------------------------- 1 | 10 | 1 | | 0 2 | 20 | 7 | | 0 3 | 30 | 3 | 3 | 0 4 | 20 | 2 | | 0 5 | 30 | 5 | 5 | 1 With Cn = if(An=max(A$1:A$5),Bn,"") and Dn = if(Cn = max(C$1:C$5), 1, 0) But I still can't find how to do it in one column
For systems without MAXIFS, put this in C1 and fill down. =--(B1=MAX(INDEX(B$1:B$5-(A$1:A$5<>MAX(A$1:A$5))*1E+99, , )))
=ARRAYFORMULA(IF(LEN(A1:A), IF(IFERROR(VLOOKUP(CONCAT(A1:A&"×", B1:B), JOIN("×", QUERY(A1:B, "order by A desc, B desc limit 1")), 1, 0), )<>"", 1, 0), )) or shorter: =ARRAYFORMULA(IF(A:A<>"",N(A:A&"×"&B:B=JOIN("×",SORTN(A:B,1,,1,0,2,0))),)) =ARRAYFORMULA(IF(A:A<>"",N(A:A&B:B=JOIN(,SORTN(A:B,1,,1,0,2,0))),))
How about the following: =--AND(A5=MAX($A$1:$A$5),B5=MAXIFS($B$1:$B$5,$A$1:$A$5,MAX($A$1:$A$5)))
Aerospike: lua udf always returns an empty result even if udf return stream without any filtering, etc
Can not understand why aggregateQuery always returns an empty result. Tried to test in aql, the same problem: 0 rows in set. Indexes are all there. aql> show indexes +---------------+-------------+-----------+------------+-------+------------------------------+-------------+------------+-----------+ | ns | bin | indextype | set | state | indexname | path | sync_state | type | +---------------+-------------+-----------+------------+-------+------------------------------+-------------+------------+-----------+ | "test" | "name" | "NONE" | "profiles" | "RW" | "inx_test_name" | "name" | "synced" | "STRING" | | "test" | "age" | "NONE" | "profiles" | "RW" | "inx_test_age" | "age" | "synced" | "NUMERIC" | aql> select * from test.profiles +---------+-----+ | name | age | +---------+-----+ | "Sally" | 19 | | 20 | | | 22 | | | 28 | | | "Ann" | 22 | | "Bob" | 22 | | "Tammy" | 22 | | "Ricky" | 20 | | 22 | | | 19 | | +---------+-----+ 10 rows in set (0.026 secs) aql> AGGREGATE mystream.avg_age() ON test.profiles WHERE age BETWEEN 20 and 29 0 rows in set (0.004 secs)
It seems that you are trying the example here. There are two problems about the udf script. I paste the code of the lua script : function avg_age(stream) local function female(rec) return rec.gender == "F" end local function name_age(rec) return map{ name=rec.name, age=rec.age } end local function eldest(p1, p2) if p1.age > p2.age then return p1 else return p2 end end return stream : filter(female) : map(name_age) : reduce(eldest) end First, there is no bin named 'gender' in your set, so you got 0 rows after aggregateQuery. Second, this script isn't doing exactly what the function name 'avg_age' means, it just return the eldest record with name and age. I paste my code bellow, it just replace the reduce func, and alert the map and filter func to meat the demand. You can just skip the filter process. function avg_age(stream) count = 0 sum = 0 local function female(rec) return true end local function name_age(rec) return rec.age end local function avg(p1, p2) count = count + 1 sum = sum + p2 return sum / count end return stream : filter(female) : map(name_age) : reduce(avg) end The output looks like bellow : AGGREGATE mystream.avg_age() ON test.avgage WHERE age BETWEEN 20 and 29 +---------+ | avg_age | +---------+ | 22 | +---------+ 1 row in set (0.001 secs)
FitClient: external process terminated before a connection could be established.
I am creating Sample Fitnesse Test and facing this error while running test fit Date: 6:34:29 PM (IST) on Tuesday, April 9, 2013 Test Page: .CalculatorRunner Command: java -cp fitnesse.jar; fit.FitServer devcode 8082 12 Exit code: -1 Time elapsed: 1.013 seconds Standard Error: Error: Could not find or load main class fit.FitServer Internal Exception: FitClient: external process terminated before a connection could be established. code at C# side namespace ConsoleApplication1 { public class CalculatorRunner : ColumnFixture { int arg1; public int Arg1 { get { return arg1; } set { arg1 = value; } } int arg2; public int Arg2 { get { return arg2; } set { arg2 = value; } } public int Sum() { return Arg1 + Arg2; } } } And Code for Fitnesse Page !path C:\..\bin\Debug\*.* !| import | | ConsoleApplication1 | !1 Let's run a simple calculator !| CalculatorRunner | | Arg1 | Arg2 | Sum? | | 0 | 0 | 0 | | 1 | 1 | 3 | | 1 | 3 | 4 | | 2 | 3 | 5 |
For C# fixtures you need a different fit server: http://fitsharp.github.io/Fit/UsingFitnesse.html
Checking if a column in a grouped set contain the same value, except the first row
I am working on a rather large query, but am now stuck on the last bit. Given this example table: Key1 | Key2 | SomeCol | 0 | 0 | ABC | 0 | 1 | 123 | ------------------------------ 1 | 5 | ABC | 1 | 6 | DEF | 1 | 7 | ABC | ------------------------------ 2 | 4 | ABC | 2 | 5 | 456 | 2 | 6 | 456 | ------------------------------ 3 | 4 | ABC | 3 | 5 | 456 | 3 | 6 | ABC | ------------------------------ 4 | 4 | ABC | 4 | 5 | ABC | 4 | 6 | ABC | At this point in my query, I have extracted sequential(Key1, Key2) portions of a table and grouped by Key1. I wish to determine if all the values of SomeCol are identical, except for the first row. Expected results: Key1 | Key2 | SomeCol | 0 | 0 | ABC | 2 | 4 | ABC | 4 | 4 | ABC | I know I can use something like.Any(g => g.SomeCol.Distinct().Count() == 1) in a case where I need all entries to be the same, but I can't seem to figure out how to get the syntax right to Skip(1). Also, I feel that my method of checking equality is sort of a hack. I know I can do this processing easily in C#, but I want to get as much of the processing to happen on the database side. Since my query is currently written in extension methods, I would appreciate in answer in the same syntax. Thanks! What I have so far: resultFromRestOfQuery .GroupBy(g => g.Key1) ???? .SelectMany(g => g.Take(1).Select(h => h)
UPDATE Alright, tested this on your values and it works. var result = collection .OrderBy(p => p.Key1) .ThenBy(p => p.Key2) .GroupBy(p => p.Key1) .Where(p => p.Skip(1) .Select(j => j.SomeCol) .Distinct().Count() == 1) .Select(p => p.First()) .ToList(); UPDATE #2 Perhaps this will help you with performance. Here is another version of this query without using Distinct(). Note the p.Count() > 1 - this is to avoid selecting the 1st row in a group when there's only 1 element in a group. If it's okay to select the first row when there's only one row, simply remove this part of the condition. var result = collection .OrderBy(p => p.Key1) .ThenBy(p => p.Key2) .GroupBy(p => p.Key1) .Where(p => p.Count() > 1 && p.Skip(1) .Select(j => j.SomeCol) .All(j => j == p.Last().SomeCol)) .Select(p => p.First()) .ToList();