I have created a timer in flutter and everything works fine. Now I can't figure out how to dismiss the timer after I started it.
The docs say that you can cancel it by calling void cancel() but I don't understand the implementation.
How am I supposed to call it?
And is it the right approach?
static const timeout = const Duration(seconds: 5);
static const ms = const Duration(milliseconds: 1);
startTimeout([int milliseconds]) {
var duration = milliseconds == null ? timeout : ms * milliseconds;
return new Timer(duration, handleTimeout);
}
void handleTimeout() { // callback function
Navigator.of(context).pushAndRemoveUntil(new MaterialPageRoute(
builder: (BuildContext context) => new ScorePage(quiz.score, quiz.length)),(Route route) => route == null);
return;
}
Just keep a reference to the time and cancel it when you don't need it anymore
var timer = startTimeout(100);
...
timer.cancel();
Related
I am trying to run something in the background while an action is performed.
This is the element that's being updated. It's linked to an object of a user defined class called spline. On dragging the point the spline object is updated and that in turn updates the curve that you see.
I am trying to write a function that runs asynchronously in the background whenever spline is updated but doesn't hinder anything else, just executes and finishes on its own time. This is the test code I've written.
useEffect(() => {
async function asyncer() {
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
const p = new Promise(function (resolve, reject) {
wait(1000);
resolve();
});
p.then(() => console.log("hello"));
}
asyncer();
}, [spline]);
But this does hinder the spline from being updated in time. Here's what happens.
The spline doesn't update until the function has completed executing.
Very simply, javascript is single threaded, and this function
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
is blocking the event loop. This prevent javascript from executing anything else. What you can do is implement this as a promise instead,
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
I have to store server-side the output of Push.getPushKey().
Is it always the same? Or is it better to make a REST request to update its value in the init()?
Can its value be null?
For example, is the following code in the init() a good approach? It waits for a not null PushKey, then it sends it and stops the timer.
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
if (Push.getPushKey() != null && authToken != null) {
Rest.post(Server.getRestServerURL() + "/updatePushKey")
.jsonContent()
.header("authToken", authToken)
.body(Push.getPushKey())
.fetchAsString((Response<String> response) -> {
if (isSuccessResponse(response)) {
Log.p("PushKey successfully sent to the server", Log.INFO);
}
});
timer.cancel();
}
}
}, 1000, 1000);
Thank you for your clarifications.
Don't use a timer. You need to store this when the registeredForPush callback is invoked. Yes, it can update and it can be null if registration failed.
How would you wait for future response for a specific amount of time?
Say, we make a http post request and await for its response before we close the http request, but, we wait for only 3 secs, else we close the request.
How would you achieve that?
Something like
Future makePostReq() async{
....
await http response for 3 secs
....
if(response) {
... Do something with it
}
Http.close
}
You can use Future.any constructor to make a race condition
final result = await Future.any([
Future.value(42),
Future.delayed(const Duration(seconds: 3))
]);
You can also use Future.timeout method
final result = await Future.value(42).timeout(const Duration(seconds: 3));
You can do it very easily
try {
var response = await Http.get("YourUrl").timeout(const Duration(seconds: 3));
if(response.statusCode == 200){
print("Success");
}else{
print("Something wrong");
}
} on TimeoutException catch (e) {
print('Timeout');
} on Error catch (e) {
print('Error: $e');
}
This example sets timeout to 3 second. If it has been 3 seconds and no response received, it will throw TimeoutException
Import this :
import 'package:http/http.dart' as Http;
import 'dart:async';
Future.any([asyncfunc, ...])
Here's an example of using Remi's Future.any solution where the future that returns first, will be used. The other is discarded.
So, the first future is your data-gathering/slow function and the other is a fallback when your call is taking too long.
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
Example in Flutter Page
Here's a copy/paste example for a Flutter page:
(look at your debug/run output window for messages)
import 'package:flutter/material.dart';
class FutureTimeoutPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Future or Timeout Page'),
),
body: FutureAnyExample(),
);
}
}
class FutureAnyExample extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Complete before timeout or timeout:'),
SizedBox(height: 30,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 1, timeout: 3),
child: Text('In Time')),
ElevatedButton(onPressed: () => getDataOrTimeout(seconds: 5, timeout: 3),
child: Text('Too Slow'))
],
)
],
);
}
Future<void> getDataOrTimeout({int seconds, int timeout}) async {
/// In Future.any, put as many async functions as you need.
/// Whichever completes first, will be returned. All others are discarded
dynamic result = await Future.any([
getData(fakeDelay: seconds), // ← hope this returns first
timeoutAfter(sec: timeout, onTimeout: () => 'Timed Out!', ) // ← waited too long, do this
]);
print(result);
}
/// Mock of a long-running operation like getting DB data, or API call
Future<String> getData({int fakeDelay}) async {
return Future.delayed(Duration(seconds: fakeDelay), () => 'Data returned!');
}
/// Do this in case my long-running op takes too long
/// Can run a function or just return some message
Future<dynamic> timeoutAfter({int sec, Function() onTimeout}) async {
return Future.delayed(Duration(seconds: sec), onTimeout);
}
}
I am new to Angular and RxJS. I wonder what is the best/correct way to consume a value from an Observable multiple times.
My setup:
I have a component which calls a service which uses a REST service.
After the server returns the result I want to
use this result in the service at hand
AND return the result to the component.
// foo.component.ts
onEvent() {
this.fooService.foo()
.subscribe((data: FooStatus) => doSomething());
}
// foo.service.ts
private lastResult: FooStatus;
constructor(protected http: HttpClient) {
}
foo(): Observable<FooResult> {
return this.http.get('/foo')
.map((data: FooStatus) => {
this.lastResult = data; // use the data...
return data; // ... and simply pass it through
});
}
Using subscribe() multiple times will not work because the request would be send multiple times. This is wrong.
At the moment I use map() to intercept the result. But I am not comfortable with this because I introduce a side effect. Seems like a code smell to me.
I experimented with
foo(onSuccess: (result: FooResult) => void, onFailure: () => void): void {
...
}
but this looks even worse, I loose the Observable magic. And I do not want to have to write these callbacks in every service method myself.
As another way I considered the call to subscribe() in the service and then to create a fresh Observable I then can return to the component. But I could not get it work... seemed to complicated, too.
Is there a more elegant solution?
Is there a helpful method on Observable I did miss?
There are a number of ways to do this, and the answer will depend on your usage.
This codepen https://codepen.io/mikkel/pen/EowxjK?editors=0011
// interval observer
// click streams from 3 buttons
console.clear()
const startButton = document.querySelector('#start')
const stopButton = document.querySelector('#stop')
const resetButton = document.querySelector('#reset')
const start$ = Rx.Observable.fromEvent(startButton, 'click')
const stop$ = Rx.Observable.fromEvent(stopButton, 'click')
const reset$ = Rx.Observable.fromEvent(resetButton, 'click')
const minutes = document.querySelector('#minutes')
const seconds = document.querySelector('#seconds')
const milliseconds = document.querySelector('#milliseconds')
const toTime = (time) => ({
milliseconds: Math.floor(time % 100),
seconds: Math.floor((time/100) % 60),
minutes: Math.floor(time / 6000)
})
const pad = (number) => number <= 9 ? ('0' + number) : number.toString()
const render = (time) => {
minutes.innerHTML = pad(time.minutes)
seconds.innerHTML = pad(time.seconds)
milliseconds.innerHTML = pad(time.milliseconds)
}
const interval$ = Rx.Observable.interval(10)
const stopOrReset$ = Rx.Observable.merge(
stop$,
reset$
)
const pausible$ = interval$
.takeUntil(stopOrReset$)
const init = 0
const inc = acc => acc+1
const reset = acc => init
const incOrReset$ = Rx.Observable.merge(
pausible$.mapTo(inc),
reset$.mapTo(reset)
)
app$ = start$
.switchMapTo(incOrReset$)
.startWith(init)
.scan((acc, currFunc) => currFunc(acc))
.map(toTime)
.subscribe(val => render(val))
You will notice that the reset$ observable is used in two other observables, incOrReset$ and stopOrReset$
You can also introduce a .multicast() operator, which will explicitly allow you to subscribe multiple times. See description here: https://www.learnrxjs.io/operators/multicasting/
Take a look at share, it allows you to share a single subscription to the underlying source. You could do something like the following (it's not tested, but should give you the idea):
private lastResult: FooStatus;
private fooObs: Observable<FooStatus>;
constructor(protected http: HttpClient) {
this.fooObs = this.http.get('/foo').share();
this.fooObs.subscribe(((data: FooStatus) => this.lastResult = data);
}
foo(): Observable<FooResult> {
return this.fooObs.map(toFooResult);
}
as mentioned by #Mikkel in his answer that approach is cool however you can make use of following methods from Observable and achieve this.
these important methods are publishReplay and refCount,
use them as below,
private lastResult: FooStatus;
private myCachedObservable : Observble<FooResult> = null;
constructor(protected http: HttpClient) {
}
foo(): Observable<FooResult> {
if(!myCachedObservable){
this.myCachedObservable = this.http.get('/foo')
.map((data: FooStatus) => {
this.lastResult = data; // use the data...
return data; // ... and simply pass it through
})
.publishReplay(1)
.refCount();
return this.myObsResponse;
} else{
return this.myObsResponse;
}
}
Now in your component simply subscribe the observable returned from your method as above and notice the network request, you will see only one network request being made for this http call.
i have a question using Observables in Silverlight 4 to make WCF calls.
Consider the simple webservice call below.
var adminclient = ServiceProxy.WithFactory<AuthenticationClient>();
var results= Observable.FromEvent<AuthorizeAdministratorCompletedEventArgs>(
s => adminclient.AuthorizeAdministratorCompleted += s,
s => adminclient.AuthorizeAdministratorCompleted -= s).ObserveOnDispatcher();
adminclient.AuthorizeAdministratorAsync();
results.Subscribe(e =>
{
//Enable UI Button
});
i have implemented an extension method, that wraps the subscribe method, it does some error validation on the return.
On the return results.Subscribe(e =>
e is System.Collections.Generic.Event<AuthorizeAdministratorCompletedEventArgs>
almost every query will have a different return type such as:
e is System.Collections.Generic.Event<AsyncCompletedEventArgs>
if i had a wrapper that looked something like this, how can i cast every type of xxxCompletedEventArgs to its base type AsyncCompletedEventArgs so that i can access e.EventArgs and inspect the Error property
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext = null, Action onError = null, Action onFinal = null)
{
Action<TSource> onNextWrapper = (s) =>
{
var args = (System.Collections.Generic.IEvent<AsyncCompletedEventArgs>)s;
try
{
if (WCFExceptionHandler.HandleError(args.EventArgs))
{
if (onNext != null)
onNext(s);
}
else
{
if (onError != null)
onError();
}
}
finally
{
if (onFinal != null)
onFinal();
}
};
return source.Subscribe<TSource>(onNextWrapper, (e) => { throw e; });
}
The code above will fail
Unable to cast object of type 'System.Collections.Generic.Event1[MyProject.Provider.AuthorizeAdministratorCompletedEventArgs]' to type 'System.Collections.Generic.IEvent1[System.ComponentModel.AsyncCompletedEventArgs]'
This is the method definition of WCFExceptionHandler.HandleError(args.EventArgs))
public static bool HandleError(AsyncCompletedEventArgs e)
I'd probably change you extension method so that it acts to handle the the events as a non blocking operator (much the same as the majority of the Rx extension method operators). Something like:
public static IObservable<IEvent<TEventArgs>> GetHandledEvents<TEventArgs>(this IObservable<IEvent<TEventArgs>> source)
where TEventArgs : AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable<IEvent<TEventArgs>>(observer =>
{
return source.Subscribe(evt =>
{
try
{
if (WCFExceptionHandler.HandleError(evt.EventArgs))
{
observer.OnNext(evt);
}
else
{
observer.OnError(new Exception("Some Exception"));
}
}
finally
{
observer.OnError(new Exception("Some Other Exception"));
}
},
observer.OnError,
observer.OnCompleted);
});
}
Then call it through:
results.GetHandledEvents()
.Finally(() => DoSomethingFinally())
.Subscribe(e =>
{
//Enable UI Button
},
ex => HandleException(ex),
() => HandleComplete());
I think this should solve your issues as the events will funnel through as their original type and it ensures that HandleError gets event args that are the correct type.