Add Single.zip() for Iterable of Singles#3539
Conversation
src/main/java/rx/Single.java
Outdated
There was a problem hiding this comment.
Although not required, I prefer iterating such sources when the child subscribes and not when the sequence is assembled.
There was a problem hiding this comment.
Hm, I am afraid that iterating over sequence when child subscribes may lead to data inconsistency if user will pass the collection of Singles and then change it before subscribe().
|
Did you consider adding Otherwise, looks good. 👍 |
|
I'll add Btw, idk if @benjchristensen remembers how I was arguing against adding |
|
Here is an implementation that avoids conversion to @SuppressWarnings("unchecked")
public static <T, R> Single<R> zip(
Iterable<? extends Single<? extends T>> singles,
FuncN<? extends R> zipper) {
return Single.create(f -> {
Single<? extends T>[] sa;
int count;
if (singles instanceof List) {
List<? extends Single<? extends T>> list =
(List<? extends Single<? extends T>>)singles;
count = list.size();
sa = list.toArray(new Single[count]);
} else {
sa = new Single[8];
count = 0;
for (Single<? extends T> s : singles) {
if (count == sa.length) {
Single<? extends T>[] sb = new Single[count + (count >> 2)];
System.arraycopy(sa, 0, sb, 0, count);
sa = sb;
}
sa[count] = s;
count++;
}
}
final AtomicInteger wip = new AtomicInteger(count);
final AtomicBoolean once = new AtomicBoolean();
final Object[] values = new Object[count];
CompositeSubscription csub = new CompositeSubscription();
f.add(csub);
for (int i = 0; i < count; i++) {
if (csub.isUnsubscribed() || once.get()) {
break;
}
final int j = i;
SingleSubscriber<T> te = new SingleSubscriber<T>() {
@Override
public void onSuccess(T value) {
values[j] = value;
if (wip.decrementAndGet() == 0) {
R r;
try {
r = zipper.call(values);
} catch (Throwable e) {
Exceptions.throwIfFatal(e);
f.onError(e);
return;
}
f.onSuccess(r);
}
}
@Override
public void onError(Throwable error) {
if (once.compareAndSet(false, true)) {
f.onError(error);
} else {
RxJavaPlugins.getInstance()
.getErrorHandler().handleError(error);
}
}
};
csub.add(te);
if (csub.isUnsubscribed() || once.get()) {
break;
}
sa[i].subscribe(te);
}
});
} |
c40010a to
0dbdc1b
Compare
|
@akarnokd I've used your code (rewrote some parts and fixed one bug) and now we don't have to convert Later we can switch other |
There was a problem hiding this comment.
This should be just this.onError to avoid error races.
0dbdc1b to
abea643
Compare
9c44746 to
1947321
Compare
|
@akarnokd changed PTAL |
src/main/java/rx/Single.java
Outdated
There was a problem hiding this comment.
Iterables and Observables are mainly for deferred data streaming. This will iterate at assembly time instead on Subscription time, losing the duality aspects. For example, an Observable source turned into an Iterable via toBlocking().getIterable() is no longer lazy when this zip is applied.
There was a problem hiding this comment.
I think it is compatible with the behavior of Observable.zip.
There was a problem hiding this comment.
Yep currently Observable serializes all values in the iterable into an array before zipping them so you can't append after lifting. IMO this is probably intentional since appending to some iterables while iterating results in ConcurrentModificationExceptions. Today the Observable<Observable<T>> overload can receive new observables to zip but the zip function won't be called until the outer observable calls onCompleted(). This is slightly later than the Iterable overload.
There was a problem hiding this comment.
So, @akarnokd, @stealthcode let's use current implementation from this PR to be consistent with Observable.zip(Iterable<Observable>)?
1947321 to
b455820
Compare
|
Rebased! |
|
Blocked by #3569. |
|
This needs to be rebased again. |
b455820 to
1a99ffd
Compare
|
👍 |
1 similar comment
|
👍 |
Add Single.zip() for Iterable of Singles
|
👍 (for posterity) |
No description provided.