In my Flutter app I'd like to make multiple network calls simultaneously and then do something when they all have finished. For this I use Future.wait(), which does what I want. However when a call fails it throws an exception, which is somehow not caught in the exception handler (i.e. uncaught exception).
When I do await _fetchSomeData() separately (outside Future.wait()) the exception does get called by the exception handler as expected.
Future<bool> someMethod() async {
try {
var results = await Future.wait([
_fetchSomeData(),
_fetchSomeOtherData()
]);
//do some stuf when both have finished...
return true;
}
on Exception catch(e) {
//does not get triggered somehow...
_handleError(e);
return false;
}
}
What do I need to do to catch the exceptions while using Future.wait()?
Update:
I have narrowed down the issue. Turns out if you use another await statement in the method that is called by the Future.wait() it causes the issue. Here an example:
void _futureWaitTest() async {
try {
//await _someMethod(); //using this does not cause an uncaught exception, but the line below does
await Future.wait([ _someMethod(), ]);
}
on Exception catch(e) {
print(e);
}
}
Future<bool> _someMethod() async {
await Future.delayed(Duration(seconds: 0), () => print('wait')); //removing this prevents the uncaught exception
throw Exception('some exception');
}
So if you either remove the await line from _someMethod() or if you just call _someMethod() outside of Future.wait() will prevent the uncaught exception. This is most unfortunate of course, I need await for an http call... some bug in Dart?
I have the Uncaught Exceptions breakpoints enabled. If I turn this off the issue seems to be gone. Perhaps it's an issue with the debugger. I am using Visual Studio Code and the latest flutter.
What do I need to do to catch the exceptions while using Future.wait()?
What I found out when I used the same code as you the code inside of each procedure which is used in Future.wait() must be wrapped with try/catch and on catch must return Future.error(). Also eagerError must be set to true.
try {
await Future.wait([proc1, ...], eagerError: true);
} on catch(e) {
print('error: $e')
}
/// Proc 1
Future<void> proc1() async {
try {
final result = await func();
} on SomeException catch(e) {
return Future.error('proc 1 error: $');
}
}
I think you are a bit mislead by the Future.wait() naming. Future.wait() returns another future that will have a List of elements returned by each future when it completes with success.
Now since the Future.wait() is still a future. You can handle it in two ways:
Using await with try catch.
Using onError callback.
Tis will be something like
Future.wait([futureOne, futureTwo])
.then((listOfValues) {
print("ALL GOOD")
},
onError: (error) { print("Something is not ok") }
Related
I have written an async Flutter/Dart function which behaves unexpectedly in my opinion. Following code structure:
static Future<bool> verifySometing() async {
try {
await getCloudData().then((snapshot) {
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
});
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
The expected result would be that the function awaits the cloud data, then awaits validation and returns true if the data is valid. In this case, the console would show the following and the function would return true:
Data is correct
What happens in practice is that the console shows the following output and the function first returns true and then false:
Data is correct
Something went wrong
This goes against anything I thought to know about funtions in Dart because I always assumed that once return is fired, the function is done. Any ideas how this happens?
The issue is with this line.
await getCloudData().then((snapshot) {
Here, instead of just awaiting, you have also attached a then callback. So in actuality, whatever you are returning is return value of the callback function ie., (snapshot) {}.
The callback function that you need to pass into the then takes that return true and gives it to us as the result of await.
So, if you would've put something like
var bool = await getCloudData().then((snapshot) { ..... });
Then this bool, would've been equal to true. That's it. No return from the your main function.
Change it to this,
var snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
Hope, I was able to explain clearly.
There are a few of faults in your assumptions.
First, you've attached a then to the future returned by getCloudData(). This means that you aren't awaiting getCloudData(), but instead the additional future returned by getCloudData().then(...), and that future will return when the callback function passed to it completes. (And unless the first future throws an error, the callback will be called.)
Second, the callback function operates on its own scope. So this code is not doing what you think it's doing:
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
This return will affect the callback function, not the verifySomething function.
Given these, the order of operation is as follows:
The validateSomething function awaits getCloudData().then(...).
getCloudData() gets called.
getCloudData() returns, the callback passed to then is called.
(Assuming the snapshot has data) validateData is called.
(Assuming data is successfully validated) "Data is correct" gets printed and the callback function returns true.
validateSomething is notified that the awaited future is complete, so execution resumes.
"Something went wrong" gets printed and the validateSomething function returns false.
Generally speaking, these kinds of errors are common when mixing async/await and then patterns. Unless you know what you're doing, stick with either one or the other, preferably the async/await pattern. For example, a refactor of your code to eliminate the call to then is as follows:
static Future<bool> verifySometing() async {
try {
final snapshot = await getCloudData();
if (snapshot.exists && snapshot.hasData) {
bool dataValid = await validateData(snapshot.data);
if (dataValid) {
print('Data is correct');
return true;
}
}
} catch (e) {
print('Error $e');
return false;
}
print('Something went wrong');
return false;
}
Now that there isn't a pesky closure to deal with, return will return from validateSomething as expected and you don't need to deal with issues like callbacks and scope.
I want to use web3.js to show revert reason to user, for example in the case of user trying to mint erc721 token that has already been minted. I am using try catch block and see the error message but I want to isolate the error message to show the user a meaningful reason. Thanks in advance.
The previous answer by #Petr Hejda didn't work for me, and neither did his suggestion in response to #Chakshu Jain's problem in the comments.
Instead, I removed some characters—from the start and the end, with slice()—that were causing the error when parsing the JSON, so I could handle the error message and get the error message.
if (err) {
var errorMessageInJson = JSON.parse(
err.message.slice(58, err.message.length - 2)
);
var errorMessageToShow = errorMessageInJson.data.data[Object.keys(errorMessageInJson.data.data)[0]].reason;
alert(errorMessageToShow);
return;
}
It's returned in the JS error object as data.<txHash>.reason.
This is a faulty Solidity code
pragma solidity ^0.8.0;
contract Test {
function foo() public {
revert('This is error message');
}
}
So a transaction calling the foo() function should revert with the message This is error message.
try {
await myContract.methods.foo().send();
} catch (e) {
const data = e.data;
const txHash = Object.keys(data)[0]; // TODO improve
const reason = data[txHash].reason;
console.log(reason); // prints "This is error message"
}
After trying out every solution on stackoverflow, random blogs, and even the officially documented "web3.eth.handleRevert = true", none is working for me.
I finally figured out after 25 failed attempts:
try {
await obj.methods.do_something().call({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
}
catch (err) {
const endIndex = err.message.search('{')
if (endIndex >= 0) {
throw err.message.substring(0, endIndex)
}
}
try {
const res = await obj.methods.do_something().send({
gasLimit: String(GAS_LIMIT),
to: CONTRACT_ADDRESS,
from: wallet,
value: String(PRICE),
})
return res.events.Transfer.returnValues.tokenId
}
catch (err) {
console.error(err)
throw err
}
The idea is to use call first. This method doesn't interact with your Metamask, but merely checks if your input arguments go through the contract method. If it can't go through, it will throw exception in the first catch block. If it does go through, we are safe to do use send. This method interacts with your Metamask for real. We have a second catch block in case there are wallet connection or gas fee issues
It is really perplexing why Solidity/Web3 don't have an easy way to extract the require/revert reason from the error object.
For me, the "require" reason is there in the message property of the error object, but it is surrounded by lot of other words which I don't need.
An example error message:
[ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert Voting is closed","code":-32000,"data":{"0xf901429f12096d3b5c23a80e56fd2230fa37411bb1f8d3cdbd5c8f91c2670771":{"error":"revert","program_counter":43,"return":"0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000165f5f5f566f74696e6720697320636c6f7365645f5f5f00000000000000000000","reason":"Voting is closed"},"stack":"RuntimeError: VM Exception while processing transaction: revert Voting is closed \n at Function.RuntimeError.fromResults (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n at BlockchainDouble.processBlock (/tmp/.mount_ganachreY1gT/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\n at runMicrotasks (<anonymous>)\n at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}'
You can see the reason Voting is closed stuck in between. Not that user-friendly to read.
I've seen answers that use regex to extract the error reason.
For those like me, who are not a big fan of the regex way, here is my approach.
In your solidity contract, wrap the require reason with a unique delimiter of sorts. In my case, it is "___" (3 underscores).
contract MyContract{
...
...
function vote(address _addr) public payable{
require(votingOpen, "___Voting closed___");
...
}
...
...
}
Declare a helper function to extract the error using JavaScript string utilities. Here's where your delimiter coes in handy.
export const extractErrorCode = (str) => {
const delimiter = '___'; //Replace it with the delimiter you used in the Solidity Contract.
const firstOccurence = str.indexOf(delimiter);
if(firstOccurence == -1) {
return "An error occured";
}
const secondOccurence = str.indexOf(delimiter, firstOccurence + 1);
if(secondOccurence == -1) {
return "An error occured";
}
//Okay so far
return str.substring(firstOccurence + delimiter.length, secondOccurence);
}
Use this function where you catch the error in your frontend
const vote = async (_addr) => {
setLoading(true);
try {
await contest.methods.vote(_addr).send({
from: accounts[0],
})
}
catch (e) {
console.log('Voting failed with error object => ', e)
console.log('Voting failed with the error => ', extractErrorCode(e.message))
}
setLoading(false);
}
Until Solidity & Web3.js (and ether.js) come out with a clean way to parse errors, we are stuck with workarounds like this.
I prefer this workaround over others because I am not that great with regex, and additionally, this one does not depend on a fixed starting position to extract the error code.
Did you try something like this?
error.toString()
It works for me just to show the revert error in the Smart Contract, and return it as a string message.
try {
//Do something
} catch (error) {
res.send({
'status': false,
'result': error.toString()
});
}
I'm the author of the Dart dshell package.
https://pub.dev/packages/dshell
Dshell is a library and tooling for writing dart cli scripts.
Dshell uses waitFor to hide futures from users as they serve little use in the typical cli application.
My problem is that if a future throws an unhandled exception whilst being handled by waitFor, it essentially shuts the application down.
I need to be able to capture any exception and then let the caller decided what to do with the exception.
Here is what I've tried so far. No of the catch techniques will capture the unhandled exception:
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwException();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwException() {
var complete = Completer<int>();
Future.delayed(Duration(seconds: 2), () => throw Exception());
return complete.future;
}
The dart waitFor has a line that makes me think this may not be possible:
If the Future completes normally, its result is returned. If the Future completes with an error, the error and stack trace are wrapped in an AsyncError and thrown. If a microtask or message handler run during this call results in an unhandled exception, that exception will be propagated as though the microtask or message handler was the only Dart invocation on the stack. That is, unhandled exceptions in a microtask or message handler will skip over stacks suspended in a call to waitFor.
So I'm a little confused by the difference between a 'Future completes with an error' and 'a microtask ... results in an unhandled exception'.
The Future returned by your throwException will never complete with either a value or an error. The error thrown by the Future.delayed is an unhandled async error, it is unrelated entirely to the Future that is returned from that method. The ways to get a Future that completes with an error are:
The Future.error constructor.
Using Completer.completeError on a not yet completed Completer.
Using throw in an async method.
Using throw in a callback passed to a Future constructor, or .then.
So in your example, the Future.delayed creates a Future that will complete with an error because of the throw in the callback. Nothing is listening on this Future. There is no await, no .then or .catchError chained off of it. Once a Future completes with an error, and it has no handlers for that error, it will bubble up to the surrounding error zone. See https://dart.dev/articles/archive/zones#handling-asynchronous-errors
If you want to be able to react to unhandled errors you can use runZoned - getting the details right can be tricky. Note that it's possible to have multiple unhandled async errors resulting from running some bit of code, and that the completion of a Future does not necessarily mean that there aren't other unhandled async errors that can surface later.
From Nate Bosch I've devised a possible answer:
I hadn't realised that you can add multiple onCatchError methods to a future.
In DShell I'm passed the future so I had assumed I couldn't modify it.
So I added an onCatchError to the Future.delayed and then use the completer to pass the error back up the correct stack.
So this seems to work, I'm just uncertain if I need to actually implement a zone to cast my catch net a little further?
import 'dart:async';
import 'dart:cli';
void main() {
var future = throwExceptionV3();
try {
future
.catchError((Object e, StackTrace st) => print('onErrr: $e'))
.whenComplete(() => print('future completed'));
print(waitFor<int>(future));
} // on AsyncError
catch (e) {
if (e.error is Exception) {
print(e.error);
} else if (e is AsyncError) {
print('Rethrowing a non DShellException ${e}');
rethrow;
} else {
print('Rethrowing a non DShellException ${e}');
rethrow;
}
} finally {
print('waitForEx finally');
}
}
Future<int> throwExceptionV3() {
var complete = Completer<int>();
try
{
var future = Future.delayed(Duration(seconds: 2), () => throw Exception());
future.catchError((Object e) {
print('caught 1');
complete.completeError('caught ') ;
});
}
catch (e)
{
print ('e');
}
return complete.future;
}
Im coming from a Java background where I use the throws keyword to lead an exception to the method calling another method. How can I do that I dart?
Method called:
void _updateCurrentUserEmail() async {
await FirebaseAuth.instance
.currentUser()
.then((FirebaseUser user) {
_email = user.email;
});
}
How it is called:
try {
_updateCurrentUserEmail();
} on Exception {
return errorScreen("No User Signed In!", barActions);
}
But it seems like the Exception is not caught, because I still get a NoSuchMethodException and the errorScreen is not shown.
While you correctly used try/catch, the exception is coming from an async function that you did not await.
try/catch only catch exceptions thrown within that block. But since you wrote:
try {
doSomethingAsyncThatWillTrowLater();
} catch (e) {
}
Then the exception thrown by the async method is thrown outside of the body of try (as try finished before the async function did), and therefore not caught.
Your solution is to either use await:
try {
await doSomethingAsyncThatWillTrowLater();
} catch (e) {
}
Or use Future.catchError/Future.then:
doSomethingAsyncThatWillTrowLater().catchError((error) {
print('Error: $error');
});
From the docs,
If the catch clause does not specify a type, that clause can handle any type of thrown object:
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e'); <------------------
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
Change it to this:
try {
_updateCurrentUserEmail();
} on Exception catch(e){
print('error caught: $e')
}
Another way to handle error is to do the following:
void _updateCurrentUserEmail() async {
await FirebaseAuth.instance
.currentUser()
.then((FirebaseUser user) {
_email = user.email;
throw("some arbitrary error");
});
.catchError(handleError);
}
handleError(e) {
print('Error: ${e.toString()}');
}
If currentUser()’s Future completes with a value, then()’s callback fires. If code within then()’s callback throws (as it does in the example above), then()’s Future completes with an error. That error is handled by catchError().
Check the docs:
https://dart.dev/guides/libraries/futures-error-handling
Throw
Here’s an example of throwing, or raising, an exception:
throw FormatException('Expected at least 1 section');
You can also throw arbitrary objects:
throw 'Out of llamas!';
throwing an exception is an expression, you can throw exceptions in => statements, as well as anywhere else that allows expressions:
void someMethod(Point other) => throw UnimplementedError();
here is example
main() {
try {
test_age(-2);
}
catch(e) {
print('Age cannot be negative');
}
}
void test_age(int age) {
if(age<0) {
throw new FormatException();
}
}
hope it helps..
What's the most performant way to parse JSON in Node.js?
What's the advantage of using process.nextTick and slightly delaying the answer in the following snippet and shouldn't the whole try/catch be encapsulated into process.nextTick?
function parseJSON(json, done) {
try {
var result = JSON.parse(json);
process.nextTick(function() { done(null, result); });
} catch (err) {
process.nextTick(function() { done(err); });
}
}
First, because JSON.parse is synchronous, there's no point in using a callback.
These two blocks of code essentially do the same thing. The bottom one is more efficient:
parseJSON(json, function(err, result) {
if (err) {
//ERROR
}
else {
//PARSED
}
});
vs
try {
var result = JSON.parse(json);
//PARSED
}
catch (e) {
//ERROR
}
Now onto your actual question(s):
What's the advantage of using process.nextTick...
Basically there is no "advantage" except that if you don't use process.nextTick then your function call becomes more synchronous. Take this example for instance:
console.log('1');
parseJSON(json, function(err, result) {
console.log('parsed');
});
console.log('2');
When using process.nextTick you'll get this output:
1
2
parsed
This happens because process.nextTick puts the callback function at the end of the event loop. Meaning it'll let other things happen first then execute the callback.
If you didn't use process.nextTick then you'd get this output:
1
parsed
2
This happens because parseJSON won't exit until the callback is called.
Both versions are valid, but with callbacks you usually to want them to be truly asynchronous.
As for the try-catch, it's fine where it's at if you're trying to catch errors coming from JSON.parse. Since you appear to want to handle the errors (because you're passing the error parameter to the callback if it throws an exception) then I'd say it's correct as is.
First of all
. process.nextick is async... Try catch may not work as you expext.. JSON.parse is preferred way to parse json string
--> What's the most performant way to parse JSON in Node.js?
JSON stream probably, becasue of the low memory footprint.
See the work done by dominictarr or mafintosh
https://github.com/mafintosh/json-format-stream
https://github.com/mafintosh/through-json
https://github.com/dominictarr/JSONStream