i am trying to understand, how dart future exception works. I read a very good article about it link. But when i have nested future and the first of them throw an error, how can i handle this error on the second future.
To clarify, what i mean look at the following sample.
import 'dart:async';
void main() {
var fur1 = new Future<int>(() => 45);
fur1.then((value) {
throw new StateError('Hello error');
});
var fur2 = new Future<int>(() => 24);
fur2.then((value) {
fur1.then((value1) {
print(value1);
});
print(value);
}).catchError((err) => print(err));
}
In the fur1, i throw an exception and expected to catch the error in the fur2, but the compiler show message
Unhandled exception: Bad state: Hello error
It is possible to handle nested error in future? I know, i could use here the completer class, maybe it would be the solution?
I'm not sure what you actually try to accomplish.
but for me it works this way
import 'dart:async';
void main() {
var fur1 = new Future<int>(() => 45);
// fur1.then((value) {
// throw new StateError('Hello error');
// });
var fur2 = new Future<int>(() => 24);
fur2.then((value) {
var x = fur1.then((value1) {
print(value1);
throw new StateError('Hello error'); // <= inner exception
});
print(value);
return x; // <= return future
}).catchError((err) => print('catchError: ${err}'));
}
or this way
import 'dart:async';
void main() {
var fur1 = new Future<int>(() => 45);
fur1.then((value) {
throw new StateError('Hello1 error');
}).catchError((err) => print('catchError1: ${err}'));
var fur2 = new Future<int>(() => 24);
fur2.then((value) {
var x = fur1.then((value1) {
print(value1);
throw new StateError('Hello2 error'); // <= inner exception
});
print(value);
return x; // <= return future
}).catchError((err) => print('catchError2: ${err}'));
}
catchError1: Bad state: Hello1 error
24
45
catchError2: Bad state: Hello2 error
Related
I am failing to understand, why the error thrown from addItem method in below code is not caught in the try-catch block
void main() async {
var executor = Executor();
var stream = Stream.fromIterable([0, 1, 2, 3, 4, 5, 6, 7]);
try {
await for (var _ in stream) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
Future<void> demoMethod() async {
var list = [1, 2, 3, 1, 4, 5];
var executor = Executor();
var test = Test();
for (var element in list) {
executor.submit(() => test.addItem(element));
}
await executor.execute();
test.list.forEach(print);
}
class Test {
var list = <int>[];
Future<void> addItem(int i) async {
if (list.contains(i)) {
throw Exception('Item exists');
}
list.add(i);
}
}
class Executor {
final List<Future<void>> _futures = [];
bool _disposed = false;
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation());
} else {
throw Exception('Executor is already disposed');
}
}
Future<void> execute() async {
await Future.wait(_futures, eagerError: true);
_disposed = true;
}
}
but below code is able to catch the error properly
void main() async {
var executor = Executor();
try {
for (var i = 0; i < 10; i++) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
I am guessing it has something to do with the stream processing.
It's the stream.
In your other examples, you synchronously run through a loop a and call Executor.submit with all the computations, then immediately call executor.execute().
There is no asychronous gap between calling the function which returns a future, and Future.wait starting to wait for that future.
In the stream code, each stream events starts an asynchronous computation by calling Executor.submit. That creates a future, stores it in a list, and goes back to waiting for the stream.
If that future completes, with an error, before the stream ends and Future.wait gets called, then there is no error handler attached to the future yet. The error is then considered unhandled, and is reported to the current Zone's uncaught error handler. Here that's the root zone, which means it's a global uncaught error, which may crash your entire program.
You need to make sure the future doesn't consider its error unhandled.
The easiest way to do that is to change submit to:
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation()..ignore());
} else {
throw StateError('Executor is already disposed');
}
}
The ..ignore() tells the future that it's OK to not have an error handler.
You know, because the code will later come back and call executor.execute, that any errors will still be reported, so it should be safe to just postpone them a little. That's what Future.ignore is for.
(Also changed Exception to StateError, because that's what you should use to report people using objects that have been disposed or otherwise decommissioned.)
I'm having trouble canceling a stream that is created using the Stream.periodic constructor. Below is my attempt at canceling the stream. However, I'm having a hard time extracting out the 'count' variable from the internal scope. Therefore, I can't cancel the subscription.
import 'dart:async';
void main() {
int count = 0;
final Stream newsStream = new Stream.periodic(Duration(seconds: 2), (_) {
return _;
});
StreamSubscription mySubscribedStream = newsStream.map((e) {
count = e;
print(count);
return 'stuff $e';
}).listen((e) {
print(e);
});
// count = 0 here because count is scoped inside mySubscribedStream
// How do I extract out 'count', so I can cancel the stream?
if (count > 5) {
mySubscribedStream.cancel();
mySubscribedStream = null;
}
}
I'd rather use take(5) instead of checking > 5 and then cancel
final Stream newsStream = new Stream.periodic(Duration(seconds: 2), (_) => count++);
newsStream.map((e) {
count = e;
print(count);
return 'stuff $e';
}).take(5).forEach((e) {
print(e);
});
EDIT: Problem wasn't related to Timer or HttpServer, it was dart.io sleep function pausing everything. It is clearly described in documentation, my bad.
//
I have weird problem with HttpClient working in server code. I call
client.getUrl(Uri.parse(url)).then((HttpClientRequest response) => response.close()).then(HttpBodyHandler.processResponse).then((HttpClientResponseBody body) {
print(body.response.statusCode);
from Timer object and it never reach print step.
It is almost copy and paste code from previous version, which wasn't called from Timer but from HttpRequest. Working code is in my question [here][1].
It fails on the long line, I suspect that it is a last Future it never reach (HttpClientResponseBody).
Timer object is created like this (just test code):
main() {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
if (new DateTime.now().hour == 17) {
print("syncing rock");
loadUrlBody(furl + filter).then((content) {
print("content loaded");
//edit:
okay, here is the source, it might be some trivial problem..which I can't figure out for two days :-D
import 'dart:async';
import 'dart:io';
import 'package:http_server/http_server.dart';
import 'package:slack/slack_io.dart' as slack;
Timer t;
bool check;
final period = 1;
final furl = "https://****.tpondemand.com";
final filter = "somefilter";
main() {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
if (new DateTime.now().hour == 17) {
print("syncing rock");
loadUrlBody(furl + filter).then((content) {
print("content loaded");
Map parsedMap = content.body;
handleMap(parsedMap);
});
sleep(new Duration(minutes: 60));
} else {
print("no time to rock " + new DateTime.now().toString());
sleep(new Duration(minutes: period * 10));
}
}
Future loadUrlBody(String url) {
final c = new Completer();
HttpClient client = new HttpClient();
client.addCredentials(Uri.parse("https://****.tpondemand.com/api"), "tprealm", new HttpClientBasicCredentials("user", "password"));
client.getUrl(Uri.parse(url)).then((HttpClientRequest response) => response.close()).then(HttpBodyHandler.processResponse).then((HttpClientResponseBody body) {
print(body.response.statusCode);
c.complete(body);
});
return c.future;
}
void send2Slack(String m) {
slack.Message message = new slack.Message()..text = m;
slack.token = 'token';
slack.team = 'team';
slack.send(message);
}
void handleMap(Map valueMap) {
final Duration lostInTime = new Duration(days: 30);
var sb = new StringBuffer();
sb.write('K o m p o s t \n');
for (var item in valueMap["Items"]) {
if (item['CreateDate'] == null) item['CreateDate'] = '/Date(1403167885000+0100)/';
if (item['ModifyDate'] == null) item['ModifyDate'] = '/Date(1403167885000+0100)/';
if (item['LastCommentDate'] == null) item['LastCommentDate'] = '/Date(1403167885000+0100)/';
DateTime moonLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['CreateDate'].substring(6, 19)));
DateTime modifyLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['ModifyDate'].substring(6, 19)));
DateTime commentLanding = new DateTime.fromMillisecondsSinceEpoch(int.parse(item['LastCommentDate'].substring(6, 19)));
DateTime lastChangeLanding = (modifyLanding.isBefore(commentLanding)) ? commentLanding : modifyLanding;
Duration difference = new DateTime.now().difference(lastChangeLanding);
if (moonLanding.add(lostInTime).isBefore(new DateTime.now()) && difference.inDays > 4) {
sb
..write('<https://****.tpondemand.com/entity/')
..write(item['Id'])
..write('|')
..write(item['Name'])
..write('> last change: ')
..write(difference.inDays)
..write(' days ago \n');
}
;
}
send2Slack(sb.toString());
print("sent to Slack");
sb.clear();
}
I created similar code but I can't reproduce your problem.
So basically this does work when called from a Timer.
import 'dart:io';
import 'dart:async';
import 'package:http_server/http_server.dart';
Timer t;
final period = 1;
void main(args) {
t = new Timer.periodic(new Duration(minutes: period), (Timer t) => hit());
}
void hit() {
loadUrlBody('http://www.google.com')
.then((HttpClientResponseBody b) => print('hit: ${b.response.statusCode}'));
}
Future loadUrlBody(String url) {
print('executing');
HttpClient client = new HttpClient();
// commented out because I have no server where I can use it
// HttpClient client = new HttpClient()
// ..addCredentials(Uri.parse("https://****.tpondemand.com/api"), "tprealm", new HttpClientBasicCredentials("user", "password"));
return client.getUrl(Uri.parse(url)) // <== return is important here
.then((HttpClientRequest response) => response.close())
.then(HttpBodyHandler.processResponse)
.then((HttpClientResponseBody body) {
print('body: (${new DateTime.now()}) ${body.response.statusCode}');
return body; // <== this value is the value the next 'then' receives.
// for example x in: loadUrlBody('http://someurl').then(x) => doSomething(x));
});
}
You don't need to use a Completer. Completer are for more complicated used cases where for example one method returns a Completer and for example an eventHandler completes it.
You just have to ensure that you return a Future everywhere. then always returns a Future. The value of the returned Future is the value returned inside then.
I have a Windows service that just silently stops on its own. Here is the relevant code:
OnStart() method:
protected override void OnStart(string[] args)
{
try
{
InitializeLogging();
// we don't DO command line arguments
if (args.Length > 0)
{
eventLog.WriteEntry("All command line arguments are ignored. You must edit the app.config file manually to make changes to what watchers are run.");
throw new ArgumentException("Command line arguments are ignored.");
}
ReadAppConfig();
RecalculateStartTimes();
InitializeWatchers();
}
catch (Exception e)
{
eventLog.WriteFormattedEntry("Error on Start: {0}", e.Message);
}
finally
{
eventLog.WriteEntry("Service start completed");
}
}
OnStop() method:
protected override void OnStop()
{
eventLog.WriteEntry("Service stopped.");
}
InitializeWatchers() method:
private void InitializeWatchers()
{
try
{
var watchers = _watcherSection.Watchers.ToList<WatcherElement>();
eventLog.WriteEntry(string.Format("Initializing {0} watchers.", watchers.Count()));
var obsWatchers = watchers.ToObservable();
obsWatchers.SelectMany(
watcher =>
Observable.Timer(watcher.StartTime, TimeSpan.FromHours(watcher.Interval))
.SelectMany(
Observable.FromAsync(
async () => new
{
watcher,
response = await CheckFolder(watcher.Path)
})))
.Subscribe(
onNext: x =>
{
eventLog.WriteFormattedEntry("\nWatcher: {0}, Time:{1}", x.watcher.Name, DateTimeOffset.Now);
if (x.response.Success)
eventLog.WriteFormattedEntry("| Success!\n| Value: '{0}'\n| Message: {0}", x.response.Value, x.response.Message);
else
eventLog.WriteFormattedEntry("| FAILURE!\n| Value: '{0}'\n| Message: {0}\n| Errors: '{0}'", x.response.Value, x.response.Message, x.response.Exceptions.First());
},
onError: e =>
{
var err = e;
var sb = new StringBuilder();
sb.AppendLine("The observer threw an error:")
.AppendFormatLine("| Message: {0}", e.Message);
while (e.InnerException != null)
{
sb.AppendFormatLine("| Inner: {0}", e.InnerException.Message);
e = e.InnerException;
}
sb.AppendLine();
eventLog.WriteEntry(sb.ToString());
throw err;
});
eventLog.WriteEntry("about to wait.");
obsWatchers.Wait();
eventLog.WriteEntry("passed the wait");
}
catch (Exception e)
{
eventLog.WriteFormattedEntry("Exception thrown in InitializeWatchers(WatchersSection): {0}", e.Message);
throw;
}
}
When I run this code, the service starts normally. The event log records three events:
Service & Logging started.
Initializing 1 watchers.
Service start completed.
... and it stops. I have to manually refresh the Services window, but it quits running. I don't get any errors, or any of the other eventLog entries.
The frustrating thing is that this code works perfectly as a Console app. I've changed all the eventLog.WriteEntry() to Console.WriteLine(), but other than that, the code is identical and performs as expected.
Any wisdom would be appreciated.
I suspect that the Service Control Manager is terminating your service because it did not return from OnStart within the timeout window (30 seconds, IIRC).
I have a blog post on managed service basics, which is based on a blog entry by the BCL team. Note that the MSDN docs are insufficient; you must know the information in the BCL team blog post to correctly write a managed service.
Instead of using obsWatchers.Wait() which blocks and causes the problems Stephen has said, just asynchronously subscribe.
Add this property to your class:
private SingleAssignmentDisposable _subscription = new SingleAssignmentDisposable();
Add this to your OnStop method:
_subscription.Dispose();
In your InitializeWatchers(), eliminate the nested call to Subscribe and replace obsWatchers.Wait() with a call to subscribe, like so:
private void InitializeWatchers()
{
try
{
var watchers = _watcherSection.Watchers.ToList<WatcherElement>();
eventLog.WriteEntry(string.Format("Initializing {0} watchers.", watchers.Count()));
var obsWatchers = watchers.ToObservable();
_subscription.Disposable = obsWatchers
.SelectMany(watcher => Observable
.Timer(watcher.StartTime, TimeSpan.FromHours(watcher.Interval))
.SelectMany(_ => Observable.FromAsync(async () => new
{
watcher,
response = await CheckFolder(watcher.Path)
})))
.Subscribe(
onNext: x =>
{
eventLog.WriteFormattedEntry("\nWatcher: {0}, Time:{1}", x.watcher.Name, DateTimeOffset.Now);
if (x.response.Success)
eventLog.WriteFormattedEntry("| Success!\n| Value: '{0}'\n| Message: {0}", x.response.Value, x.response.Message);
else
eventLog.WriteFormattedEntry("| FAILURE!\n| Value: '{0}'\n| Message: {0}\n| Errors: '{0}'", x.response.Value, x.response.Message, x.response.Exceptions.First());
},
onError: e =>
{
var err = e;
var sb = new StringBuilder();
sb.AppendLine("The observer threw an error:")
.AppendFormatLine("| Message: {0}", e.Message);
while (e.InnerException != null)
{
sb.AppendFormatLine("| Inner: {0}", e.InnerException.Message);
e = e.InnerException;
}
sb.AppendLine();
eventLog.WriteEntry(sb.ToString());
throw err;
});
eventLog.WriteEntry("passed the wait");
}
catch (Exception e)
{
eventLog.WriteFormattedEntry("Exception thrown in InitializeWatchers(WatchersSection): {0}", e.Message);
throw;
}
}
I encountered the following example (Example 1 below) of Futures which caused me to wonder if I could alter the way that I was handling Futures and remove all of the nested function calls that preserve order of processing, which however result in indentation which I find a bit messy.
The altered version of my program did not work however. It did not preserve the order of processing and did not “wait” for function to complete. For example, before returning from the first call (fGetUserInput), another subsequent function was called.
Why is it that in Example 1, all of the “1st level” “new Future”s processed sequentially, however in Example 2, my altered code, the order of processing is not preserved. While the call to fGetUserInput is being processed, one of the Futures that follows it is processed?
Is it perhaps that “Example 1” only “works” because all of the statements are synchronous?
I came across a reference to “runAsync”. Can that be used to achieve what I want? (process in sequence without all of the indentation).
// Example 1. Code that I encountered for Futures //
import 'dart:async';
main() {
new Future(() => print('1'))
.then((_) => print('a'))
.then((_) => print('b'));
new Future(() => print('2'))
.then((_) => print('c'))
.then((_) => print('d'));
new Future(() => print('3'))
.then((_) =>
new Future(() => print('e'))
.then((_) => print('f'))
);
new Future(() => print('4'))
.then((_) =>
new Future(() => print('g'))
.then((_) => print('d'))
);
}
The above results in the following console output order :-
1 a b 2 c d 3 4 e f g d
Which I thought made sense.
Therefore, I modified my code to test it as follows :-
// Example 2. Altered version of my code which //
// does not preserve the order of processing, //
// which is necessary for program to function. //
new async.Future(() => fGetUserInput())
.then((lInput) {
iMaxIters = int.parse(lInput[4]);
tClearTable = (lInput[5] == "y");
iDivisor = fInitialize(iMaxIters);
tgPrint = false; // printing off
sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sStartTime = lInput[7];
})
.catchError((oError) => fFatal("Get User Input", oError));
new async.Future(() => fConnectToDb(sUri, sStartTime))
.then((bool tConnected) {
if (ogDb == null)
fFatal("Unable to connect to database", "");
print ("Processing database ......");
})
.catchError((oError) => fFatal("Connect to Db", oError));
new async.Future(() => fClearTable(tClearTable))
.then((sResult) => print (sResult+"\n"))
.catchError((oError) => fFatal("Clear Table", oError));
new async.Future(() => fProcessInserts(iMaxIters, iDivisor))
.then((sResult) => print (""))
.catchError((oError) => fFatal("Process Inserts", oError));
new async.Future(() => fSetupRandKeys())
.then((sResult) => print (""))
.catchError((oError) => fFatal("Setup Random Keys", oError));
new async.Future(() => fProcessUpdates(iMaxIters, iDivisor))
.then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
})
.catchError((oError) => fFatal("Process Updates", oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("\nProcessing Inserts ......");
fResetAndStartWatch();
The following is my code prior to the above changes, and the following Example 3 appears to work OK. I don't like the extent of indentation, and in situations with more function calls, that would increase the extent of indentation. I was hoping for a more elegant way to do it.
// Example 3: The original version of my code //
// which does preserve the order of processing //
void main() {
print("");
String sCheckPoint = "Get User Input";
fGetUserInput()
.then((lInput) {
int iMaxIters = int.parse(lInput[4]);
bool tClearTable = (lInput[5] == "y");
int iDiv = fInitialize(iMaxIters);
tgPrint = false; // printing off
String sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sCheckPoint = "Connect to Database";
fConnectToDb(sUri, lInput[7]).then((bool tConnected) {
if (ogDb == null)
fFatal(sCheckPoint, "Unable to conenct to Db");
print ("Processing database ......");
sCheckPoint = "Clear Table";
fClearTable(tClearTable).then((sResult) {
print (sResult+"\n");
sCheckPoint = "Process Inserts";
fProcessInserts(iMaxIters, iDiv).then((sResult) {
print;
sCheckPoint = "Set-up Random Keys";
fSetupRandKeys().then((sResult) {
print;
sCheckPoint = "Process Updates";
fProcessUpdates(iMaxIters, iDiv).then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
});
});
});
});
});
})
.catchError((oError) => fFatal(sCheckPoint, oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("Processing Inserts ......");
fResetAndStartWatch();
Remember that you can chain futures, which will reduce your indentation by quite a bit.
The downside is that you don't get nested scopes, which can be useful if you have more than one value to propagate between async blocks, but that can be worked around in a few ways.
Here's you example 3 with chaining:
// Example 3 with chaining
void main() {
String checkPoint = "Get User Input";
getUserInput().then((input) {
int maxIters = int.parse(input[4]);
bool clearTable = (input[5] == "y");
int div = initialize(maxIters);
shouldPrint = false; // printing off
String uri =
"postgres://${input[1]}:${input[2]}#localhost:5432/${input[3]}";
checkPoint = "Connect to Database";
return connectToDb(uri, input[7]).then((bool connected) {
if (db == null)
fatal(checkPoint, "Unable to conenct to Db");
print ("Processing database ......");
checkPoint = "Clear Table";
return clearTable(shouldClearTable);
}).then((result) {
print (result+"\n");
checkPoint = "Process Inserts";
return processInserts(maxIters, div);
}).then((result) {
print('');
checkPoint = "Set-up Random Keys";
return setupRandKeys();
}).then((result) {
print('');
checkPoint = "Process Updates";
return processUpdates(maxIters, div);
}).then((result) {
String totValue = formatAmount(grandTotAmt, true, 2);
print("Grand Total added to database = \$${totValue}");
return db.close();
// exit(0); pretty much never call exit()
});
}).catchError((error) => fatal(checkPoint, error));
}
Edit: Oops, looking more closely I got bit by the scoping problem... I added a level of nesting just to capture the needed vars in a scope accessible by the following blocks. I'm also removing the hungarian-ish notation, because... don't do that in Dart :)