My actual requirement:
I have a list of custom objects, and I want to iterate it with a delay. I can't use DispatchQueue.main.asyncAfter in my for loop since my iterations create a CoreData object that triggers FetchedResultController and hence updates my TableView. Anyway, so I tried using Rx to iterate my list with a delay of 1 second each. But I am unable to do so.
Question
I want to delay the iteration of each element of the array using RxSwift.
I was able to do it in Java, but couldn't do so in RxSwift.
the .delay() operator didn't help either, it just delayed the whole process.
Any example would help, thus I am not posting any specific code... but this is what I've been trying so far
var array = [1, 2, 3, 4, 5]
Observable.from(array)
.delay(RxTimeInterval(5), scheduler: MainScheduler.instance)
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
Output
onNext() next(1)
onNext() next(2)
onNext() next(3)
onNext() next(4)
onNext() next(5)
onNext() completed
The output gets printed after 5 seconds, not with 5 seconds interval.
I am not getting any integer values, but a next(1).
This is a common confusion. As you have learned delay will be applied to every element equally so all of them are delayed by five seconds, rather than putting a five second delay between each event. (So the first event happens immediately, the second at five seconds, the third at ten, and so on.
Once you realize that what you are really trying to do is put a delay between each event, the solution becomes more clear. Or at least, the reason why just putting a delay on the from operator isn't working should be more clear.
The solution is to use one of the flatMap variants:
let array = [1, 2, 3, 4, 5]
Observable.from(array)
.concatMap { Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0) }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
The line Observable.empty().delay(.seconds(5), scheduler: MainScheduler.instance).startWith($0) will immediately emit the value, but then wait five seconds before emitting a completed event.
The concatMap operator calls the closure on each incoming event and concats the resulting Observables together so that following Observables aren't subscribed to, until the previous one completes.
Learn more about this in my article, The Many Faces of FlatMap
An alternative solution based on the comments that Sandeep Bhandari wrote on the question would be to use an interval to constrain when the from operator can emit values. Something like this:
let array = [1, 2, 3, 4, 5]
Observable.zip(Observable.from(array), Observable<Int>.interval(.seconds(5), scheduler: MainScheduler.instance).take(while: { $0 != array.count }))
.map { $0 }
.subscribe { (intValue) in
print("onNext() \(intValue)")
}
Related
In Objective-C and Swift, is there any guarantee of order of execution for concurrent calls being made inside of a serial queue's async block?
Pseudo-code:
let serialQueue = SerialQueue()
let concurrentQueue = ConcurrentQueue()
serialQueue.async { // 1
concurrentQueue.async { // 2
run_task1() // task that takes 1 hour
}
}
serialQueue.async { // 3
concurrentQueue.async { // 4
run_task2() // task that takes 1 minute
}
}
In the above code, is task1 guaranteed to complete before task2 is called?
Or since they're called on a concurrent thread, the serial queue async only guarantees that run_task1 will be added to the concurrentQueue before run_task2, but not guarantee order of execution?
I've numbered the block in your question, so I can reference them here:
Block 1 and 3 are both running on a serial queue, thus block 3 will only run once 1 is done.
However, block 1 and 3 don't actually wait for task1/2, they just queue off work to happen asynchronously in blocks 2 and 4, which finishes near instantly.
From then on, both task 1 and 2 will be running concurrently, and finish in an arbitrary order. The only guarantee is that task1 will start before task2.
I always like to use the analogy of ordering a pizza vs making a pizza. Queuing async work is like ordering a pizza. It doesn't mean you have a pizza ready immediately, and you're not going to be blocked from doing other things while the pizzeria is baking your pizza.
Your blocks 1 and 3 are strongly ordered, so 1 will finish and finish before 3 starts. However, all the block does is order a pizza, and that's fast. It does mean pizza 1 (task 1) is done before pizza 2 (task 2), it just means you got off the first phone call before making the second.
I am attempting to understand how Swift handles for-in loops.
Overview: we are iterating over the rows of an NSOutlineView. If a condition is met, we expand the item, which obviously changes the overall row count of the outlineView.
Pre-Condition: the OutlineView has 5 "root" items. Each of those has 5 child items.
Example
final class anOutlineView: NSOutlineView
{
override func reloadData()
{
super.reloadData()
for i in 0 ..< self.numberOfRows
{
// Assume we expand the item at row 0, which increases
// the overall outlineView row count from 5 to 10.
}
}
}
In this approach, the loop stops when i == 4. I assume that's because Swift evaluates the range only once, the first time it encounters it? Is there a way to change that behavior so that the conditions are re-evaluated each time through the loop, like a traditional for loop?
Replacing the for loop with a while loop obviously works and is a fine solution. I'm simply trying to understand the nuances of Swift because this behavior is not what I expected. In Objective-C, the for loop conditions were evaluated on each iteration and it was a well-known performance optimization to refrain from calling self.property in loop conditions (unless a good reason existed, as it does in this case.)
0 ..< self.numberOfRows is a Range and in particular a Sequence. Iterating over a sequence is done by creating an iterator, and then calling its next() method until the iterator is exhausted, compare IteratorProtocol:
Whenever you use a for-in loop with an array, set, or any other collection or sequence, you’re using that type’s iterator. Swift uses a sequence’s or collection’s iterator internally to enable the for-in loop language construct.
So
for i in 0 ..< self.numberOfRows {
...
}
is equivalent to
let range = 0 ..< self.numberOfRows
var it = range.makeIterator()
while let i = it.next() {
...
}
Modifying numberOfRows during the iteration does not mutate the range (which is a value type) or the iterator, and therefore does not affect the number of iterations.
Let's say I have an array of elements [1, 2, 3] and a delay of 5 seconds.
I want to emit each element of the array with a pause between the current element emited and the next.
Example:
Output:
[00:00] -- 1
[00:05] -- 2
[00:10] -- 3
I've tried to do the following:
import ReactiveSwift
let startTime = DispatchTime.now().uptimeNanoseconds
let arrayProperty = MutableProperty<[Int]>([1, 2, 3])
let arraySignal = arrayProperty.signal
arraySignal
.flatMap { $0 }
.delay(2, on: QueueScheduler.main)
.observeValues { element in
let elapsed = DispatchTime.now().uptimeNanoseconds
print("\((elapsed - startTime) / 1_000_000_000) -- \(element)")
}
But it only delays the emission of the first element and emits the next ones immediately.
I couldn't find the proper operator mix to use, maybe they are missing on ReactiveSwift framework, and I'm a beginner in Reactive programming so implementing my own operator is way too hard yet.
I haven’t had a chance to try this code, but it should be something like this:
SignalProducer([1, 2, 3])
.flatMap(.concat) { n in
return SignalProducer(value: n).delay(2, on: QueueScheduler.main)
}
.startWithValues { element in
let elapsed = DispatchTime.now().uptimeNanoseconds
print("\((elapsed - startTime) / 1_000_000_000) -- \(element)")
}
The key is that you use flatMap to create a new signal producer for each value which you can apply the delay to, and combine them with the .concat flatten strategy.
(Also, note you can use the signal producer initializer that takes a sequence)
Apologies if the question is poorly phrased, I'll do my best.
If I have a sequence of values with times as an Observable[(U,T)] where U is a value and T is a time-like type (or anything difference-able I suppose), how could I write an operator which is an auto-reset one-touch barrier, which is silent when abs(u_n - u_reset) < barrier, but spits out t_n - t_reset if the barrier is touched, at which point it also resets u_reset = u_n.
That is to say, the first value this operator receives becomes the baseline, and it emits nothing. Henceforth it monitors the values of the stream, and as soon as one of them is beyond the baseline value (above or below), it emits the elapsed time (measured by the timestamps of the events), and resets the baseline. These times then will be processed to form a high-frequency estimate of the volatility.
For reference, I am trying to write a volatility estimator outlined in http://www.amazon.com/Volatility-Trading-CD-ROM-Wiley/dp/0470181990 , where rather than measuring the standard deviation (deviations at regular homogeneous times), you repeatedly measure the time taken to breach a barrier for some fixed barrier amount.
Specifically, could this be written using existing operators? I'm a bit stuck on how the state would be reset, though maybe I need to make two nested operators, one which is one-shot and another which keeps creating that one-shot... I know it could be done by writing one by hand, but then I need to write my own publisher etc etc.
Thanks!
I don't fully understand the algorithm and your variables in the example, but you can use flatMap with some heap-state and return empty() or just() as needed:
int[] var1 = { 0 };
source.flatMap(v -> {
var1[0] += v;
if ((var1[0] & 1) == 0) {
return Observable.just(v);
}
return Observable.empty();
});
If you need a per-sequence state because of multiple consumers, you can defer the whole thing:
Observable.defer(() -> {
int[] var1 = { 0 };
return source.flatMap(v -> {
var1[0] += v;
if ((var1[0] & 1) == 0) {
return Observable.just(v);
}
return Observable.empty();
});
}).subscribe(...);
I need to debounce an input-stream.
At the first occurrence of state 1 I need to wait for 5 Seconds and verify if the laste state was also 1.
Only than I have a stable signal.
(time) 0-1-2-3-4-5-6-7-8-9
(state) 0-0-0-0-0-1-0-1-0-1
(result) -> 1
Here is an example of a non-stable signal.
(time) 0-1-2-3-4-5-6-7-8-9
(state) 0-0-0-0-0-1-0-1-0-0
(result) -> 0
I tried using a buffer, but a buffer has fixed starting point and I need to wait for 5 seconds starting with my first event.
Taking your requirements literally
At the first occurrence of state 1 I need to wait for 5 Seconds and
verify if the laste state was also 1. Only than I have a stable
signal.
I can come up with a few ways to solve this problem.
To clarify my assumptions, you just want to push the last value produced 5 seconds after the first occurrence of a 1. This will result in a single value sequence producing either a 0 or a 1 (ie. regardless of any further values produced past 5 seconds from the source sequence)
Here I recreate you sequence with some jiggery-pokery.
var source = Observable.Timer(TimeSpan.Zero,TimeSpan.FromSeconds(1))
.Take(10)
.Select(i=>{if(i==5 || i==7 || i==9){return 1;}else{return 0;}}); //Should produce 1;
//.Select(i=>{if(i==5 || i==7 ){return 1;}else{return 0;}}); //Should produce 0;
All of the options below look to share the sequence. To share a sequence safely in Rx we Publish() and connect it. I use automatic connecting via the RefCount() operator.
var sharedSource = source.Publish().RefCount();
1) In this solution we take the first value of 1, and then buffer the selected the values of the sequence in to buffer sizes of 5 seconds. We only take the first of these buffers. Once we get this buffer, we get the last value and push that. If the buffer is empty, I assume we push a one as the last value was the '1' that started the buffer from running.
sharedSource.Where(state=>state==1)
.Take(1)
.SelectMany(_=>sharedSource.Buffer(TimeSpan.FromSeconds(5)).Take(1))
.Select(buffer=>
{
if(buffer.Any())
{
return buffer.Last();
}
else{
return 1;
}
})
.Dump();
2) In this solution I take the approach to only start listening once we get a valid value (1) and then take all values until a timer triggers the termination. From here we take the last value produced.
var fromFirstValid = sharedSource.SkipWhile(state=>state==0);
fromFirstValid
.TakeUntil(
fromFirstValid.Take(1)
.SelectMany(_=>Observable.Timer(TimeSpan.FromSeconds(5))))
.TakeLast(1)
.Dump();
3) In this solution I use the window operator to create a single window that opens when the first value of '1' happens and then closes when 5 seconds elapses. Again we just take the last value
sharedSource.Window(
sharedSource.Where(state=>state==1),
_=>Observable.Timer(TimeSpan.FromSeconds(5)))
.SelectMany(window=>window.TakeLast(1))
.Take(1)
.Dump();
So lots of different ways to skin-a-cat.
It sounds (at a glance) like you want Throttle, not Buffer, although some more information on your use cases would help pin that down - at any rate, here's how you might Throttle your stream:
void Main()
{
var subject = new Subject<int>();
var source = subject.Publish().RefCount();
var query = source
// Start counting on a 1, wait 5 seconds, and take the last value
.Throttle(x => Observable.Timer(TimeSpan.FromSeconds(5)));
using(query.Subscribe(Console.WriteLine))
{
// This sequence should produce a one
subject.OnNext(1);
subject.OnNext(0);
subject.OnNext(1);
subject.OnNext(0);
subject.OnNext(1);
subject.OnNext(1);
Console.ReadLine();
// This sequence should produce a zero
subject.OnNext(0);
subject.OnNext(0);
subject.OnNext(0);
subject.OnNext(0);
subject.OnNext(1);
subject.OnNext(0);
Console.ReadLine();
}
}