I am using flowable iterator and process the request for each item from the iterable. If any exception thrown how to find out for which input the exception occurs.
e.g:
Flowable.fromIterable(userList)
.flatMap(d -> Flowable.fromCallable(() -> getClaimStatus(d))
).map(d -> updateClaimStatus(d))
.subscribe(d -> System.out.println("Processed"),
err -> System.err.println(err.getMessage()));
I just want to print for which user the error occurs.
You could try-catch inside the fromCallable/map and throw a wrapper exception of your choice:
Flowable.fromIterable(userList)
.flatMap(d -> Flowable.fromCallable(() -> {
try {
return getClaimStatus(d);
} catch (Exception ex) {
throw new Exception("User: " + d, ex);
}
}))
.map(d -> {
try {
return updateClaimStatus(d);
} catch (Exception ex) {
throw new Exception("User: " + d, ex);
}
})
.subscribe(
d -> System.out.println("Processed"),
err -> System.err.println(err.getMessage())
);
Related
Why first case prints :
"second level catch"
"top level catch"
and second case prints:
only "second level catch"?
Difference in finally block.
Future<void> main() async {
try {
secondLevelTryCatch();
} catch (e, s) {
print("top level catch");
}
}
void secondLevelTryCatch() {
try {
throw Exception();
} catch (e, s) {
print("second level catch");
rethrow;
} finally {
//1 no return
//2 return;
}
//3return;
}
why 3 case print: "second level catch" "top level catch"
this code sample
import 'package:flutter_test/flutter_test.dart';
void main() {
const one = 1;
String oneAsString() => one as String;
test('Make sure it fails', () {
var s;
var _e;
try {
s = oneAsString();
} on Error catch (e) {
_e = e;
}
expect(s == null, true, reason: 'should fail');
expect(_e == null, false, reason: 'error or exception should be returned');
});
test('Catchin Error', () {
var _e;
try {
oneAsString();
} on Error catch (e) {
_e = e;
}
expect(_e is Error, true, reason: 'should be an error');
});
test('Catchin Exception', () {
var _e;
try {
oneAsString();
} on Exception catch (e) {
_e = e;
}
expect(_e is Exception, true, reason: 'should be an exception');
});
test('Why not BOTH', () {
var _e;
try {
oneAsString();
} on Error catch (e) {
_e = e;
} on Exception catch (e) {
_e = e;
}
expect(_e is Error, true, reason: 'should be an error');
expect(_e is Exception, false, reason: 'should NOT be an exception');
});
}
outputs this result
00:02 +2 -1: Catchin Exception [E]
type 'int' is not a subtype of type 'String' in type cast
test/widget_test.dart 5:31 main.oneAsString
test/widget_test.dart 31:18 main.<fn>
00:02 +3 -1: Some tests failed.
the safest approach seems to be Why not BOTH, but following effective dart
(enforcing it with the package effective_dart)
the analysis complains about avoid_catching_errors
DON'T explicitly catch Error or types that implement it.
Errors differ from Exceptions in that Errors can be analyzed and
prevented prior to runtime. It should almost never be necessary to
catch an error at runtime.
BAD:
try { somethingRisky(); } on Error catch(e) { doSomething(e); }
GOOD:
try { somethingRisky(); } on Exception catch(e) { doSomething(e);
}
and if I simple do
try {} catch (e) {}
the analyzer complains about avoid_catches_without_on_clauses
AVOID catches without on clauses.
Using catch clauses without on clauses make your code prone to
encountering unexpected errors that won't be thrown (and thus will go
unnoticed).
BAD:
try { somethingRisky() } catch(e) { doSomething(e); } GOOD:
try { somethingRisky() } on Exception catch(e) { doSomething(e); }
what is the correct approach to catch BOTH Error and Exception?
re-posting the answer from #Miyoyo on r/FlutterDev discord
Optimally? You do not catch error
Just, at all
Remember effective dart is Guidelines
Using them as gospel will make some things impossible
Catching error is deliberately made to be "not according to guidelines"
because you're not supposed to do that
for my use case I'll just add
// ignore_for_file: avoid_catching_errors
I want to catch an exception if the app fails to connect to the server (If the server is turned off for example) but not sure how and didn't succeed so far.
My code:
static Future<String> communicate(String img, String size) async
{
String request = size.padLeft(10, '0') + img;
Socket _socket;
await Socket.connect(ip, 9933).then((Socket sock)
{
_socket = sock;
}).then((_)
{
//Send to server
_socket.add(ascii.encode(request));
return _socket.first;
}).then((data)
{
//Get answer from server
response = ascii.decode(base64.decode(new String.fromCharCodes(data).trim()));
});
return response;
}
Function call:
var ans = await communicate(bs64Image, size);
In general, you handle errors like this with async/await:
try {
// code that might throw an exception
}
on Exception1 {
// exception handling code
}
catch Exception2 {
// exception handling
}
finally {
// code that should always execute; irrespective of the exception
}
In your case, you should try something like:
try {
var ans = await communicate(bs64Image, size);
}
catch (e){
print(e.error);
}
finally {
print("finished with exceptions");
}
Try using SocketException if request fails exception will be thrown
import 'dart:io';
try {
response = await get(url);
} on SocketException catch (e) {
return e;
}
I have two reactive methods in "Layer A" that's exposed to the app:
LAYER A - publicly accessible methods to the rest of the app
public Single<ResponseData> postMyData(ReqData data, Long id) {
if (!isUserLoggedIn()) return postLogin().andThen(postData(data, id));
return postData(data, id);
}
public Completable postLogin() {
Account account = getAccountData();
ReqLoginData loginData = new ReqLoginData(account.email, account.pass, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET);
Single<ResponseLogin> singlePostLogin = postLogin(loginData);
return Completable.create(subscriber -> singlePostLogin.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(loginResponse -> {
// Success
storeAccessToken(loginResponse);// *** this will recreate apiClient with new access token
if (!subscriber.isDisposed()) subscriber.onComplete();
}, throwable -> { if (!subscriber.isDisposed()) subscriber.onError(throwable);}
));
}
LAYER B - only accessible by Layer A
public Single<ResponseLogin> postLogin(ReqLoginData loginData) {
Log.d(TAG, "using apiClient "+apiClient.toString());
Single<Response<ResponseLogin>> postLogin = apiClient.postLogin(loginData);
return Single.create(subscriber -> postLogin.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(response -> {
if(response.body() != null) {
if (!subscriber.isDisposed()) subscriber.onSuccess(response.body());
} else {
if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
}
}, throwable -> {
throwable.printStackTrace();
if(!subscriber.isDisposed()) subscriber.onError(throwable);
}));
}
public Single<ResponseData> postData(ReqData data, Long id) {
Log.d(TAG, "using apiClient "+apiClient.toString());
Single<Response<ResponseData>> postData = apiClient.postData(id, data);
return Single.create(subscriber -> postData.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(response -> {
if(response.body() != null) {
if (!subscriber.isDisposed()) subscriber.onSuccess(response.body());
} else {
if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
}
}, throwable -> {
throwable.printStackTrace();
if(!subscriber.isDisposed()) subscriber.onError(throwable);
}));
}
LAYER C - API Interface
#Headers({"Content-Type: application/json"})
#POST(Constants.API_PREFIX + "/auth/token/")
Single<Response<ResponseLogin>> postLogin(#Body ReqLoginData reqLoginData);
#Headers({"Content-Type: application/json"})
#POST(Constants.API_PREFIX + "/data/{id}/")
Single<Response<ResponseData>> postData(#Path("id") Long id, #Body ReqData reqData);
A huge challenge for me at the moment is to make sure the user is logged-in before attempting to POST data. So, using postLogin().andThen(postData(data, id)) made sense to me :
At the time of my late-night development, it seemed that both methods postLogin() and postData() are called in parallel - with postLogin() first.
How can I make postMyData() execute postLogin() first, wait for success or failure, followed by postData() on success ?
Note: postLogin() will update apiClient with the new token inside storeAccessToken(). I now see that postData() doesn't use a reference to the new apiClient. It's important that postData() will use the new apiClient that's instantiated within storeAccessToken()
Sorry for the newbie question :-(
it turned out that Completable.andThen(Single) works very well to serialize code execution. Unfortunately, I didn't (half asleep) realize that only code within the create() method was actually scheduled.
my code below answers the problem I had (these are both in LAYER-B):
public Single<ResponseLogin> postLogin(ReqLoginData loginData) {
return Single.create(subscriber -> {
Log.d(TAG, "using apiClient "+apiClient.toString());
apiClient.postLogin(loginData)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(response -> {
if(response.isSuccessful() && response.body() != null) {
if (!subscriber.isDisposed()) subscriber.onSuccess(response.body());
} else {
if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
}
}, throwable -> {
throwable.printStackTrace();
if(!subscriber.isDisposed()) subscriber.onError(throwable);
});
});
}
public Single<ResponseData> postData(ReqData data, Long id) {
return Single.create(subscriber -> {
Log.d(TAG, "using apiClient "+apiClient.toString());
apiClient.postData(id, data)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(response -> {
if(response.body() != null) {
if (!subscriber.isDisposed()) subscriber.onSuccess(response.body());
} else {
if (!subscriber.isDisposed()) subscriber.onError(new Throwable(response.message()));
}
}, throwable -> {
throwable.printStackTrace();
if(!subscriber.isDisposed()) subscriber.onError(throwable);
});
});
}
The context is using Couchbase to implement a REST CRUD service on a 2-level document store. The data model is an index document pointing to zero or more item documents. The index document is retrieved as an Observable using an asynchronous get. This is followed by a .flatMap() that retrieves zero or more IDs for each item document. The async get returns an Observable, so now the Observable I'm creating is Observable>. I want to chain a .merge() operator that will take "an Observable that emits Observables, and will merge their output into the output of a single Observable" to quote the ReactiveX documentation :) Then I will .subscribe() to that single Observable to retrieve item documents. The .merge() operator has a many signatures, but I can't figure out how to use it in a chain of operators as follows:
bucket
.async()
.get(id)
.flatMap(
document -> {
JsonArray itemArray = (JsonArray) document.content().get("item");
// create Observable that gets and emits zero or more
// Observable<Observable<JsonDocument>> ie. bucket.async().gets
Observable<Observable<JsonDocument>> items =
Observable.create(observer -> {
try {
if (!observer.isUnsubscribed()) {
itemArray.forEach(
(jsonObject) -> {
String itemId = ((JsonObject)jsonObject).get("itemid").toString();
observer.onNext(
bucket.async().get(itemId)
);
}
}
);
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
);
return items;
},
throwable -> {
// error handling omitted...
return Observable.empty();
},
() -> {
// on complete processing omitted...
return null;
}
)
.merge( ???????? )
.subscribe(
nextItem -> {
// do something with each item document...
},
throwable -> {
// error handling omitted...
},
() -> {
// do something else...
}
);
EDIT:
You probably guessed I'm a reactive newbie. The answer from #akarnokd helped me realise what I was trying to do was dumb. The solution is to merge the emissions from the items Observable<Observable<JsonDocument>> inside the document closure and return the result of that. This emits the resulting JsonDocuments from the flatMap:
bucket
.async()
.get(id)
.flatMap(
document -> {
JsonArray itemArray = (JsonArray) document.content().get("item");
// create Observable that gets and emits zero or more
// Observable<Observable<JsonDocument>> ie. bucket.async().gets
Observable<Observable<JsonDocument>> items =
Observable.create(observer -> {
try {
if (!observer.isUnsubscribed()) {
itemArray.forEach(
(jsonObject) -> {
String itemId = ((JsonObject)jsonObject).get("itemid").toString();
observer.onNext(
bucket.async().get(itemId)
);
}
}
);
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
);
return Observable.merge(items);
},
throwable -> {
// error handling omitted...
return Observable.empty();
},
() -> {
// on complete processing omitted...
return null;
}
)
.subscribe(
nextItem -> {
// do something with each item document...
},
throwable -> {
// error handling omitted...
},
() -> {
// do something else...
}
);
Tested and works :)
Due to expressive limits of Java, we can't have a parameterless merge() operator that can be applied on an Observble<Observable<T>>. It would require extension methods such as in C#.
The next best thing is to do an identity flatMap:
// ...
.flatMap(document -> ...)
.flatMap(v -> v)
.subscribe(...)
You can call toList() to collect all emitted items into one list. I've not tested it but what about something like this:
bucket.async()
.get(id)
.flatmap(document -> { return Observable.from((JsonArray)document.content().get("item")})
.flatMap(bucket::get)
.toList()
.subscribe(results -> /* list of documents */);