I have this Streamsubscription in a Stateless widget
StreamSubscription < LocationResult > subscription =
Geolocation.locationUpdates(
accuracy: LocationAccuracy.best,
displacementFilter: 30.0, // in meters
inBackground: true,)
.listen((result) {
if (result.isSuccessful) {
saveResult(result);
} else {
}
});
I call this in a timer
startTimeout(mins) async {
await subscription.resume();
print("susbscription started");
return new Timer(Duration(minutes: mins), handleTimeout);
}
void handleTimeout() async{ // callback function
await subscription.cancel();
print("susbscription canceled");
}
The timer is called on button click:
startTimeout(1);
After a minute susbscription canceled is printed but the Geolocator keeps getting called.
Future cancel ()
Cancels this subscription.
After this call, the subscription no longer receives events.
The stream may need to shut down the source of events and clean up
after the subscription is canceled.
Returns a future that is completed once the stream has finished its
cleanup.
For historical reasons, may also return null if no cleanup was
necessary. Returning null is deprecated and should be avoided.
Typically, futures are returned when the stream needs to release
resources. For example, a stream might need to close an open file (as
an asynchronous operation). If the listener wants to delete the file
after having canceled the subscription, it must wait for the cleanup
future to complete.
A returned future completes with a null value. If the cleanup throws,
which it really shouldn't, the returned future completes with that
error.
Related
I'm trying to use android live data to observe completion status of an async task in my viewmodel from my fragment. So i considered using ObserveOnce from this post LiveData remove Observer after first callback
// in ViewModel
var status= MutableLiveData<Boolean>()
fun asyncTask(){
// do some async task
asyncTask.addOnSuccessListener{
status.value = true
}
asyncTask.addOnFaiureListener{
status.value = false
}
}
// In Fragment
fun startProcess(){
viewmodel.status.value = false
viewmodel.asyncTask
viewmodel.status.observeOnce(viewLifecycleOwner, Observer { it ->
if(it){
Toast.maketext(requireActivity(),"Task Done",Toast.LENGTH_SHORT).maketext()
}else{
Toast.maketext(requireActivity(),"Task Failed",Toast.LENGTH_SHORT).maketext()
}
})
}
The problem here is that this observeOnce is called immediately after initilization, and is always showing false.
I dont understand what is wrong here!!
You actually set a value for your status which will be emitted as soon as the observer will have an active state.
If LiveData already has data set, it will be delivered to the observer.
...
When data changes while the owner is not active, it will not receive any updates. If it becomes active again, it will receive the last available data automatically.
Do not set the initial value unless you need a default to start with.
Is it possible to cancel Firebase HTTPS Callable function during it's request?
I have a function with some predictive search. It is called each time when user inputs a character in a search field.
Code:
func startSearch(_ query: String, completion: #escaping (_ results: [SearcheResults]) -> Void) {
let data = [
"query": query
]
functions.httpsCallable("startSearch").call(data) { (result, error) in
if error != nil {
completion([])
} else if let data = result?.data {
// some data manipulations
completion(elements)
}
}
}
Or maybe somehow dismiss earlier completions? Because for now, if user is very rapid and enter text, for example "Berlin" - completion will fire 6 times. I'd like to have a way to cancel a function or cancel previous completions.
Thanks in advance.
You should try debounce, basically in debounce before you fire a request you wait for short span(eg:- 2 secs), and if user types in that span again, timer is reset to again 2 secs,check the link
Once the call is made it cannot be cancelled, It will Either get executed to completition or timedout.
Once you invoke a callable function, it can't be canceled. The function will run to completion or timeout. You will need to be sure on the client that you really want to invoke the function. You are by no means obliged to consume the result (you can ignore it if you want), but the transaction will complete, unless the client app dies in the process. In that case, the function on the backend will still complete, but it will just not be able to deliver the response.
I have a couple of operations to perform on the IoT device from iOS App. So All my operations are in OperationsQueue with serial operations.
Here I want to perform one operation at a time and each operation needs to wait until I get a response from the IoT device.
Here IoT device response will take time to send back. so how to wait for the current operation in operation queue until I get a response from IoT.
So is there any way to pause current running operation until getting a response from IoT and then I will resume it so that the next operation in operation queue will start.
I tried with Sleep operation But it required time, but we can not guarantee about IoT device response.
Any suggestions would appreciate it. Thank you in advance.
The basic idea is that you don’t pause (or wait, or sleep), but rather you define a “concurrent” operation (see discussion of concurrent operations in the documentation) that doesn’t trigger the isFinished KVO until the device responds.
A simple way to do this is to write a concurrent operation class, like the one shown in this answer. Then your IoT operation can subclass that AsynchronousOperation class, and just call finish() when the device responds.
Then your operation queue (which presumably has a maxConcurrentOperationCount of 1, or perhaps is using dependencies), will not start an operation until the prior operation has finished.
As Rob said, you can implement Asynchronous Operation class and subclass from it your IoT operation. For me it looks like the most preferred way to implement yr case.
As an alternative, in cases where you need to continue the process only after some asynchronous event in another thread completed, you can use NSCondition. This is a mechanism from obj-c that provide an easy way to wait for a condition to occur.
Here is example:
let cond = NSCondition()
var available = false
var sharedString = ""
class WriterThread: Thread {
override func main() {
for _ in 0..<5 {
cond.lock()
sharedString = "😅"
available = true
cond.signal() // Notify and wake up the waiting thread/s
cond.unlock()
}
}
}
class PrinterThread: Thread {
override func main(){
for _ in 0..<5 { //Just do it 5 times
cond.lock()
while(!available) { //Protect from spurious signals
cond.wait()
}
print(SharedString)
sharedString = ""
available = false
cond.unlock()
}
}
}
let writet = WriterThread()
let printt = PrinterThread()
printt.start()
writet.start()
You could use a DispatchQueue and call .suspend() when you send the operation, and have the code that gets the response call .resume(). Then wherever you want to wait for the response before continuing, just put a dummy queue.sync({ print("done waiting")}) and it will automatically wait until .resume() has been called before printing and continuing.
import Dispatch
var queue = DispatchQueue(label: "queue")
func sendOperationToIoTDevice(){
//send the operation
//...
queue.suspend()
}
...
//whatever code gets the response:
//get response
//...
queue.resume()
...
//main code
sendOperationToIoTDevice()
queue.sync { print("done waiting") } // will hang here until .resume() is called
I am calling one API every 10 sec using Observable.timer(0,10000) and unsubscribing on ngOnDestroy() method.But this call is not getting stopped and its keep getting adds in the queue and calling even after leaving that component.
I have tried to destroy the component on ngOnDestroy() function. But it's still its getting call.
it should have called only on this component and should call once every 10 seconds but if we go to other component call is getting added in the queue and once come back to instance component it is calling multiple time every 10 secs.
Code:
this._instanceSub = Observable.timer(0,reference.INSTANCE_CALL_INTERVAL) .subscribe(() => { this.getComonentInstance(this.componentId); }); ngOnDestroy() {
if (this._instanceSub) {
this._instanceSub.unsubscribe();
}
The above code must work i have used the below code for the same
this.subject = new Subject();
ngOnInit() {
timer(0, reference.INSTANCE_CALL_INTERVAL).pipe(
takeUntil(this.subject),
).subscribe(t => this.getComonentInstance(this.componentId));
}
and in ngOndestroy
ngOnDestroy() {
this.subject.next();
}
I have a timer that is called from a button OnPressed: startTimeOut(60).
startTimeout(mins) async {
await subscription.resume();
print("susbscription started");
return new Timer(Duration(minutes: mins), handleTimeout);
}
void handleTimeout() async {
await subscription.cancel().then((_) {
print("susbscription canceled");
});
But the user has the possibility to abort the subscription stream by calling startTimeOut(0). In this case "susbscription canceled" get's printed but the first timer call is still active, so the subscription goes on merrily till the 60 minutes are over. Then "susbscription canceled" is printed again.
How could I sort of overwrite/cancel the first call to Timer?
You will need to keep a reference to the Timer instance and call cancel on subsequent calls to startTimeout.