import 'dart:async';
void main() {
divide(1, 0).then((result) => print('1 / 0 = $result'))
.catchError((error) => print('Error occured during division: $error'));
}
Future<double> divide(int a, b) {
if (b == 0) {
throw new Exception('Division by zero');
}
return new Future.value(a/b);
}
Currently I am learning how to work with futures in Dart and I got stucked on such an easy example. My future throws exception when user tries to perform division by zero. However, .catchError doesn't handle my exception. I got unhandled exception with a stack trace instead. I'm pretty sure that I am missing something obvious but can't understand what exactly.
As I understand, there is another way to handle error:
divide(1, 0).then((result) => print('1 / 0 = $result'),
onError: (error) => print('Error occured during division: $error'));
to use named optional argument - onError. Doing like this still leads to unhandled exception.
I would like to clarify one more thing. Am I right? - The only difference between these 2 approaches is that .catchError() also handles errors thrown by inner futures (futures that are called inside then() method of the outer future) while onError only catches errors thrown by the outer future?
Dmitry
Thank you.
Your error handling didn't work because the error is thrown in the synchronous part of your code. Just because the method returns a future doesn't mean everything in this method is async.
void main() {
try {
divide(1, 0)
.then((result) => print('1 / 0 = $result'));
} catch (error) {
print('Error occured during division: $error');
}
}
If you change your divide function like
Future<double> divide(int a, b) {
return new Future(() {
if (b == 0) {
throw new Exception('Division by zero');
}
return new Future.value(a / b);
});
}
you get an async error and your async error handling works.
An easier way is to use the new async/await
main() async {
try {
await divide(1, 0);
} catch(error){
print('Error occured during division: $error');
}
}
try at DartPad
and another advantage is this works in both cases.
Related
If I have nested async* streams, exceptions do not appear to be catchable, which is counterintuitive.
An example:
void main() {
getString().listen(print);
}
Stream<String> getString() async* {
try {
yield* asyncStarError();
yield await asyncError();
} catch (err) {
yield 'Crash';
}
}
Stream<String> asyncStarError() async* {
throw Exception('A Stream error happened');
}
Future<String> asyncError() async {
throw Exception('A Future error happened');
}
This outputs:
Uncaught Error: Exception: A Stream error happened
Crash
So the exception thrown by asyncStarError is not caught, while the Future is caught as expected. Can anyone explain why?
You can observe the behaviour in dartpad: https://dartpad.dartlang.org
The yield* forwards all events from the stream it yields, including errors.
So, asyncStarError() produces a stream with an error event, and yield* forwards that error to the stream returned by getString, and then the getString.listen(print); does not add a handler so the error becomes uncaught.
(I'm guessing you're running this in a browser since that uncaught error doesn't crash the program.)
After that, the yield await asyncError() never yield anything. The await asyncError() itself throws, before reaching the yield, and that error is then caught by the catch which yields crash.
If you want to catch the errors of a stream, you need to actually look at the events, not just use yield* to forward them all blindly, including error events.
For example:
await for (var _ in asyncStarError()) {
// Wohoo, event!
}
would make the error from the asyncStarError stream an error at the loop. It would then be caught and you'd print "Crash".
TL;DR: The yield* operation forwards error events, it doesn't raise them locally.
Since yield * forwards them like #lrn said, you can catch them in main just fine. Or wherever you are consuming them.
void main() {
getString().listen(print).onError((e) => print('error caught: $e'));
}
Stream<String> getString() async* {
try {
yield* asyncStarError();
yield await asyncError();
} catch (err) {
yield 'Crash';
}
}
Stream<String> asyncStarError() async* {
throw Exception('A Stream error happened');
}
Future<String> asyncError() async {
throw Exception('A Future error happened');
}
this prints:
error caught: Exception: A Stream error happened
Crash
The difference is that async generator (async*) exceptions cannot be caught by surrounding it with try/catch.
Instead, you should use the callback handleError to achieve the behaviour you are looking for.
To go further, you may be interested in using runZonedGuarded.
In Electron if I throw an error anywhere on the backend it goes to a custom window. Trying to find a way to catch that to push to a custom area in my app I've found that I can detect the process with process.on('uncaughtException'). However I'm stuck trying to run a sender to send either the error or the report. What I've tried:
ipcMain.on('main', async (e, data) => {
try {
await someModule(data)
process.on('uncaughtException', err => e.sender.send('error', err.message))
return e.sender.send('audit', 'No issues found')
} catch (err) {
console.log(err)
}
})
module.js:
module.export = data => {
throw Error('this is a test')
}
In the above I'm sending both get both errorandaudit` to renderer. I've researched for a way to pass 'uncaughtException' to a ternary but I'm not able to find any docs on how to condition for 'uncaughtException' but I did try:
process.on('uncaughtException', err => {
if (err) return e.sender.send('error', err.message)
return e.sender.send('audit', 'test complete')
})
and the above only works if an error is present, research:
Catch all uncaughtException for Node js app
Nodejs uncaught exception handling
Node.js Uncaught Exception - Passing more details
In Electron how can I intercept the error to pass it to renderer from main without throwing the default error window?
If you use ipcMain.handle you will be able to handle errors in the renderer process like this
// Main process
ipcMain.handle('my-invokable-ipc', async (event, data) => {
await someModule(data)
return 'No issues found'
})
// Renderer process
async () => {
try {
const result = await ipcRenderer.invoke('my-invokable-ipc', data)
console.log(result) // 'No issues found' if someModule did not throw an error
} catch (err) {
// you can handle someModule errors here
}
}
Update: An issue with this approach is that the error emitted to the renderer process is serialized and it gets printed even though it's handled with a try/catch.
To fix this, you can also handle the errors in the main process
// Main process
ipcMain.handle('my-invokable-ipc', async (event, data) => {
try {
await someModule(data)
return 'No issues found'
} catch (err) {
// handle someModule errors and notify renderer process
// return err.message or any other way you see fit
}
})
// Renderer process
async () => {
const result = await ipcRenderer.invoke('my-invokable-ipc', data)
console.log(result) // 'No issues found' if someModule did not throw an error
}
I want to put in a handler that will catch all unhandled errors in a Polymer app.
I figured Zone would be the trick so tried
void main() {
runZoned(() => initPolymer(), onError: (e, stackTrace) {
_log.shout('TOP ZONE', e, stackTrace);
});
}
But that doesn't work. The errors never get to this error handler.
Not sure if this relates to http://code.google.com/p/dart/issues/detail?id=15854
How do people handle this?
How about using Window.onError.
import 'dart:html';
main() {
window.onError.listen((ErrorEvent e) => print(e.message));
throw 'boom!';
}
So I know we have gotten error handling to work using the following construct:
runZoned(() {
return initPolymer().run(() => Polymer.onReady
.then(doSomeStuff)
.whenComplete(doSomeCompleting));
},
onError: (err, [stackTrace]) {
logger.severe("Received an error", err, stackTrace);
});
I have posted it in the interest of helping you quickly. I dont have a great explanation off the top of my head why your version isn't working at the moment. Ill do some digging and see if I can work out what is really different.
I use my postgres database query to determine my next action. And I need to wait for the results before I can execute the next line of code. Now my conn.query returns a Future but I can't manage to get it async when I place my code in another function.
main() {
// get the database connection string from the settings.ini in the project root folder
db = getdb();
geturl().then((String url) => print(url));
}
Future geturl() {
connect(db).then((conn) {
conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
.then((result) { return result[0].toString(); })
.catchError((err) => print('Query error: $err'))
.whenComplete(() {
conn.close();
});
});
}
I just want geturl() to wait for the returned value but whatever I do; it fires immediately. Can anyone point me a of a piece of the docs that explains what I am missing here?
You're not actually returning a Future in geturl currently. You have to actually return the Futures that you use:
Future geturl() {
return connect(db).then((conn) {
return conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
.then((result) { return result[0].toString(); })
.catchError((err) => print('Query error: $err'))
.whenComplete(() {
conn.close();
});
});
}
To elaborate on John's comment, here's how you'd implement this using async/await. (The async/await feature was added in Dart 1.9)
main() async {
try {
var url = await getUrl();
print(url);
} on Exception catch (ex) {
print('Query error: $ex');
}
}
Future getUrl() async {
// get the database connection string from the settings.ini in the project root folder
db = getdb();
var conn = await connect(db);
try {
var sql = "select trim(url) from crawler.crawls where content IS NULL";
var result = await conn.query(sql).toList();
return result[0].toString();
} finally {
conn.close();
}
}
I prefer, in scenarios with multiple-chained futures (hopefully soon a thing of the past once await comes out), to use a Completer. It works like this:
Future geturl() {
final c = new Completer(); // declare a completer.
connect(db).then((conn) {
conn.query("select trim(url) from crawler.crawls where content IS NULL").toList()
.then((result) {
c.complete(result[0].toString()); // use the completer to return the result instead
})
.catchError((err) => print('Query error: $err'))
.whenComplete(() {
conn.close();
});
});
return c.future; // return the future to the completer instead
}
To answer your 'where are the docs' question: https://www.dartlang.org/docs/tutorials/futures/
You said that you were trying to get your geturl() function to 'wait for the returned value'. A function that returns a Future (as in the example in the previous answer) will execute and return immediately, it will not wait. In fact that is precisely what Futures are for, to avoid code doing nothing or 'blocking' while waiting for data to arrive or an external process to finish.
The key thing to understand is that when the interpreter gets to a call to then() or 'catchError()' on a Future, it does not execute the code inside, it puts it aside to be executed later when the future 'completes', and then just keeps right on executing any following code.
In other words, when using Futures in Dart you are setting up chunks of code that will be executed non-linearly.
Following the tutorial on DartWatch blog on using Google OAuth library. The question is how to handle: 'Access denied' error from Google ?
Here is my code example:
class Client
{
GoogleOAuth2 _auth;
Client()
{
_auth = new GoogleOAuth2(
'5xxxxxxxxxxxxxx.apps.googleusercontent.com', // Client ID
['openid', 'email', 'profile'],
tokenLoaded:oauthReady);
}
void doLogin()
{
// _auth.logout();
// _auth.token = null;
try
{
_auth.login();
}
on AuthException {
print('Access denied');
}
on Exception catch(exp)
{
print('Exception $exp occurred');
}
}
void oauthReady(Token token)
{
print('Token is: $token');
}
}
but I never hit catch block on any (!) exception. What I'm doing wrong ?
I'm using:
Dart Editor version 0.5.0_r21823
Dart SDK version 0.5.0.1_r21823
You never hit the catch block because auth.login is an asynchronous operation which returns a Future.
There is a great article on Future error handling on the dartlang.org website.
auth.login returns a Future immediately, but the work it does happens after control returns to the event loop (see my answer to another question for more on the event loop.)
Your code should look more like:
/// Returns a Future that completes to whether the login was successful.
Future<boolean> doLogin()
{
_auth.login()
.then((_) {
print("Success!");
return true;
}.catchError((e) {
print('Exception $e occurred.');
return false; // or throw the exception again.
}
}