q.js automatically propagate errors (catch async thrown errors)? - q

i would like to know if there's any way to automatically propagate errors from a promise to another? IE: catch the thrown error from a nested promise.
for example, in the following code sample, the "internalWorker" nested promise function needs
.fail(function (error) {
return deferred.reject(error);
});
in order to propagate the error. if this line isn't contained, the error is throw to the top. (crashed app)
would it be possible to automatically propagate the error so i don't need to add .fail() functions to all my nested promises?
```
function top(input) {
var deferred = q.defer();
internalWorker(input).then(function (value) {
logger.inspectDebug("top success", value);
}).fail(function (error) {
return deferred.reject(error);
});
return deferred.promise;
}
function internalWorker(input) {
var deferred = q.defer();
q.delay(100).then(function () {
throw new Error("internal worker async error");
}).fail(function (error) {
return deferred.reject(error);
});
return deferred.promise;
}
top("hello").then(function (value) {
logger.inspectDebug("outside success", value);
}).fail(function (error) {
logger.inspectDebug("outside fail", error);
}).done();
```

If you are using https://github.com/kriskowal/q, this will do what you intend:
function top(input) {
return internalWorker(input).then(function (value) {
logger.inspectDebug("top success", value);
return value;
});
}
function internalWorker(input) {
return q.delay(100).then(function () {
throw new Error("internal worker async error");
return value;
});
}
top("hello").then(function (value) {
logger.inspectDebug("outside success", value);
}, function (error) {
logger.inspectDebug("outside fail", error);
}).done();
Return promises or values from within callbacks. Errors propagate implicitly.

Related

How can "A value of type 'void' can t be returned by the 'onError' handler..." on Future.catchError() be avoided?

Here's the simple Dart code which works, but generate a compiler warning:
Future<String> fetchUserOrder() {
return Future.delayed(Duration(seconds: 2), () {
throw UnsupportedError('out of coffee');
// return 'ristretto';
});
}
void main() {
print('main: start');
fetchUserOrder()
.then((value) {
print(value);
})
.catchError((error) => print(error.message)) // DISPLAYED WARNING: A value of type 'void' can t be returned by the 'onError' handler because it must be assignable to 'FutureOr<Null>'.
.whenComplete(() => print('complete'));
}
How is it possible to avoid the compiler warning "A value of type 'void' can t be returned by the 'onError' handler because it must be assignable to 'FutureOr'." displayed on the catchError line ?
The direct way is to use an async function:
void main() async {
print('main: start');
try {
var value = fetchUserOrder();
print(value);
} catch (error) {
print((error as dynamic).message);
} finally {
print('complete'));
}
}
If you want to use Future methods instead, you can make the then call return a Future<void> as:
void main() {
print('main: start');
fetchUserOrder()
.then<void>((value) {
print(value);
})
.catchError((error) => print(error.message))
.whenComplete(() => print('complete'));
}
I'd probably personally go with including the catchError in the then call:
void main() {
print('main: start');
fetchUserOrder().then<void>((value) {
print(value);
}, onError: (dynamic error) {
print(error.message);
}).whenComplete(() => print('complete'));
}
Using then allows you to set the required return type of the onError callback, and it can be used even when there is no value callback, as:
someFuture.then<void>((_){}, onError: (e, s) {
do something without returning a value;
});

Why can't catch exceptions with "catchError" in Dart?

Test:
void testAs() async {
try {
String b = await test();
print(b);
} catch (e) {
print("1 await error");
}
test().then((value) => print(value)).catchError(() {
print("2 then error");
});
}
Future<String> test() {
List<String> bb = ["2222"];
return Future.value(bb[1]);
}
1 await error
RangeError (index): Invalid value: Only valid value is 0: 1
Why is it ineffective?
If I want to deal with "future" through "then", how should I catch the exception and not let it throw out.
Thank you friends,the last problem has been solved,the problem can be solved by adding async and await flags to the test() method.
But there is a new problem, Now I use the correct code and find that it can only be printed once.why can't it print "then success",then the program ends
,modify as follows:
void testAs() async {
try {
await test();
print("await success");
} catch (e) {
print("await error");
}
test().then((value) => print("then success")).catchError((e) {
print("then error");
});
}
Future<String> test() async{
List<String> bb = ["2222"];
return await Future.value(bb[0]);
}
print:await success

Dart : Make reusable try catch block for error handling

I have simple function login request to server, in this function i have some error handling .
Example Source Code
try {
final response = await http.post(
'${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
headers: appConfig.headersApi,
body: {
"username": username,
"password": password,
},
);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "ok") {
List userList = responseJson['data'];
List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
return result;
} else {
throw CustomError(responseJson['message']);
}
} on SocketException catch (_) {
return Future.error(ConstText.NO_CONNECTION);
} on TimeoutException catch (_) {
return Future.error(ConstText.TIMEOUT_EXCEPTION);
} on FormatException catch (_) {
return Future.error(ConstText.FORMAT_EXCEPTION);
} catch (e) {
return Future.error(e.toString());
}
}
In above source code, I have 4 Handling error like SocketException,TimeoutException,FormatException and UnknownException. This function work fine, but if i create another function for request server i should repeat the error handling again.
My question is , it's possible to make error handling reusable ? I want something like this.
Example Reusable Try Catch
requestServer(yourRequestServer) async{
try{
return yourRequestServer;
}on SocketException catch (_) {
return Future.error(ConstText.NO_CONNECTION);
} on TimeoutException catch (_) {
return Future.error(ConstText.TIMEOUT_EXCEPTION);
} on FormatException catch (_) {
return Future.error(ConstText.FORMAT_EXCEPTION);
} catch (e) {
return Future.error(e.toString());
}
}
How To Use it
Future<String> testLogin(String username,String password)async{
requestServer({
final response = await http.post(
'${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
headers: appConfig.headersApi,
body: {
"username": username,
"password": password,
},
);
final Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "ok") {
List userList = responseJson['data'];
List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
return result;
} else {
throw CustomError(responseJson['message']);
}
});
}
Or if you have another suggestion , i really appreciate that.
Thank's.
Reusing code is always a matter of figuring out what to keep an what to abstract away.
In your case, you want to reuse the catch clauses.
The thing you are abstracting over is the body of the try clause, which appears to contain some asynchronous code.
Since you are abstracting over code, you'll need to pass in a function. That's the only way to make code into an argument value.
So you'll need something like:
Future<T> requestServer(FutureOr<T> computation()) {
try {
return await computation();
} on SocketException catch (_) {
throw ConstText.NO_CONNECTION;
} on TimeoutException catch (_) {
throw ConstText.TIMEOUT_EXCEPTION;
} on FormatException catch (_) {
throw ConstText.FORMAT_EXCEPTION;
} catch (e) {
throw e.toString();
}
}
You can then use it as:
var result = await requestServer(() {
final response = await http.post(...
...
return result;
...
});
I changed the return Future.error(someString); to throw someString because it's the same thing, and the latter is much more readable. You are throwing strings here, not exception or error objects. That's a bold move, but as long as you are the one to catch them again, it's reasonable. It's not a good API for other people to have to catch.

Susbcribe to many promises?

I'm new to angular and I'm facing a problem where I need to call several promises and get all their results prior to continue the process.
// Let's assume this array is already populated
objects: any[];
// DB calls
insertObject(obj1: any): Promise<any> {
return this.insertDB('/create.json', obj1);
}
updateObject(obj: any): Promise<any> {
return this.updateDB('/update.json', obj);
}
// UI invokes this:
save(): void {
this.insertObject(objects[0])
.then((result) => {
console.log(result.data[0].id);
})
.catch((reason) => {
console.debug("[insert] error", reason);
});
this.insertObject(objects[1])
.then((result) => {
console.log(result.data[0].id);
})
.catch((reason) => {
console.debug("[insert] error", reason);
});
this.updateObject(objects[1])
.then((result) => {
console.log(result.data[0].status);
})
.catch((reason) => {
console.debug("[update] error", reason);
});
//I need to catch these 3 results in order to perform the next action.
}
Any ideas on how to achieve that?
Using the syntax Promise.all(iterable), you can execute an array of Promises. This method resolves when all promises have resolved and fails if any of those promises fail.
This can help you
let firstPromise = Promise.resolve(10);
let secondPromise = Promise.resolve(5);
let thirdPromise = Promise.resolve(20);
Promise
.all([firstPromise, secondPromise, thirdPromise])
.then(values => {
console.log(values);
});

Get one database record, display it, update it, and save it back to the database

SOLVED! It was a Knockout issue (wrong binding). But maybe someone likes to argue or comment about the code in general (dataservice, viewmodel, etc).
I tried to build a Breeze sample, where I get one database record (with fetchEntityByKey), display it for updating, then with a save button, write the changes back to the database. I could not figure out how to get it to work.
I was trying to have a dataservice ('class') and a viewmodel ('class'), binding the viewmodel with Knockout to the view.
I very much appreciated if someone could provide a sample or provide some hints.
Thankx, Harry
var dataservice = (function () {
var serviceName = "/api/amms/";
breeze.NamingConvention.camelCase.setAsDefault();
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName,
entityManager: entityManager,
init: init,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function init() {
return getMetadataStore();
}
function getMetadataStore() {
return entityManager.fetchMetadata()
.then(function (result) { return dataservice; })
.fail(function () { window.alert("fetchMetadata:fail"); })
.fin(function () { });
}
function saveChanges() {
return entityManager.saveChanges()
.then(function (result) { return result; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () { window.alert("fetchEntityByKey:fail"); })
.fin(function () { });
}
})();
var viewmodel = (function () {
var viewmodel = {
location: null,
error: ko.observable(""),
init: init,
saveChanges: null
};
return viewmodel;
function init() {
return dataservice.init().then(function () {
viewmodel.saveChanges = dataservice.saveChanges;
return getLocation();
})
}
function getLocation() {
return dataservice.getLocation().then(function (result) {
return viewmodel.location = result;
})
}
})();
viewmodel.init().then(function () {
ko.applyBindings(viewmodel);
});
Glad you solved it. Can't help noticing that you added a great number of do-nothing callbacks. I can't think of a reason to do that. You also asked for metadata explicitly. But your call to fetchEntityByKey will do that implicitly for you because, as you called it, it will always go to the server.
Also, it is a good idea to re-throw the error in the fail callback within a dataservice so that a caller (e.g., the ViewModel) can add its own fail handler. Without re-throw, the caller's fail callback would not hear it (Q promise machinery acts as if the first fail handler "solved" the problem).
Therefore, your dataservice could be reduced to:
var dataservice = (function () {
breeze.NamingConvention.camelCase.setAsDefault();
var serviceName = "/api/amms/";
var entityManager = new breeze.EntityManager(serviceName);
var dataservice = {
serviceName: serviceName, // why are you exporting this?
entityManager: entityManager,
saveChanges: saveChanges,
getLocation: getLocation
};
return dataservice;
function saveChanges() {
return entityManager.saveChanges()
.fail(function () {
window.alert("saveChanges failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
function getLocation() {
return entityManager.fetchEntityByKey("LgtLocation", 1001, false)
.then(function (result) { return result.entity; })
.fail(function () {
window.alert("fetchEntityByKey failed: " + error.message);
throw error; // re-throw so caller can hear it
})
}
})();
I don't want to make too much of this. Maybe you're giving us the stripped down version of something more substantial. But, in case you (or a reader) think those methods are always necessary, I wanted to make clear that they are not.

Resources