pass an errormessage from server to client - dart

I defined some class to query a database.
class SqlGetData {
ConnectionPool pool;
List<String> rows;
SqlGetData(this.pool);
Future <List<String>> run(String sQuery) {
rows = new List<String>();
return readData(sQuery).then((_) {
return rows;
});
}
Future readData(String sQuery) {
return pool.query(sQuery).then((result) {
return result.forEach((row) {
String s = JSON.encode(row);
rows.add(s);
});
});
}
}
which I call like this:
var sql = new SqlGetData(pool);
sql.run('select firstName, lastName from person where id =' + s1).then((rows) {
some code here to process the data
});
If the database is not running I get an error on the return pool.query in readData, which I want to catch and pass to the client in some error message.
How and where can I code the try ... catch ... to prevent the server from dying? My problem is that I have to return futures, which is still difficult for me to grasp.

Take a look at this article Futures and Error Handling (if you haven't already).
There are two places:
.then((_) => doSomething(),
onError: (e) => doErrorHandling()).catchError((e) => doErrorHandling());

Guenter's answer is good. Here are a couple of extra tips.
It's more common to use .catchError() than the named parameter, if in doubt just use .catchError().
One problem with async code is if you forget to add a catchError handler anywhere in your codebase, and an error is triggered, it will bring your whole server down. Not good. However You can use Zones to handle uncaught errors in your code, and prevent this from happening.
There isn't much documentation about Zones at the time of writing, as it is a new feature. Florian Loitsch is working on an article which will appear here sometime soon. Here is an example of using runZoned():
runZoned(() {
var pool = new Pool.connect(...); // Don't know pool API, just making this up.
pool.query(sql).then((result) {
print(result);
});
// Oops developer forgot to add a .catchError() handler for the query.
// .catchError((e) => print('Query error: $e);
}, onError: (e) => print("Uncaught error: $e"));
This code will run without bringing down your server, despite the missing catchError() handler. Note, you will need to start the pool/connection within the same zone as the query is executed within.

Related

Electron: Can same channel name use for ipcMain.on and ipcMain.handle?

Can we register the same channel for ipcMain.on method and ipcMain.handle()?
For eg:
ipcMain.handle('test', async(event,args) => {
let result = await somePromise();
return result;
});
ipcMain.on('test', async(event,args) => {
event.returnValue = await somePromise();
});
Will the above code, give the error No handler register for 'test'? if ipcRenderer called this via invoke and sendSync in an order?
for eg:
ipcRenderer.invoke('test', data).then(result => {
console.log(result);
return result;
});
someFunction(data) {
return ipcRenderer.sendSync('test', data);
}
This is one of those things that you can easily test out.
Looking at their code for ipcMain.handle, they store the channel name in an _invokeHandlers map that seems isolated from the rest of the module (meaning from ipcMain.on).
In fact, ipcMain extends an EventEmitter, which is a Node class that maintains its own internal structure for managing events (This is the module where on and once are defined).
So you should be able to safely use both ipcMain.on("test", ...) and ipcMain.handle("test", ...) as long as you trigger them using the appropriate mechanism: send/sendSync corresponds with on/once and invoke corresponds with handle/handleOnce.

dart - correct coding pattern for subscription when using null saftey?

I've enabled the dart 2.8 experimental null saftey.
I have the following exiting code.
StreamSubscription<String> subscription;
subscription =
response.transform(Utf8Decoder()).transform(LineSplitter()).listen(
(line) async {
result += line;
},
onDone: () async {
unawaited(subscription.cancel());
completer.complete(result);
},
);
With null saftey enabled I get a error in the 'onDone' method where it calls subscription.cancl
"The expression is nullable and must be null-checked before it can be used.
Try checking that the value isn't null before using it.",
I can fix the problem by putting a conditional before the call to cancel, but this seems unnecessary as in reality subscription can never be null.
Is there a coding pattern that allows subscription to be declared as non-null?
The problem here is that the read of subscription happens at a place where it's still potentially unassigned. It isn't, actually, but we only know that because the listen method promises not to call any of the callbacks before returning. The compiler can't see that. So, you need to move the reading to after the assignment.
What I'd do to make this listen call work:
var buffer = StringBuffer(result);
var subscription = response
.transform(Utf8Decoder())
.transform(LineSplitter())
.listen((line) {
buffer.write(line);
});
subscription.onDone(() {
completer.complete(buffer.toString());
});
I removed the async from the callbacks because it is not needed. All it does to make these functions async is to return a future that no-one would ever look at.
In general, the callbacks on Stream and Future should have non-async callbacks.
I also removed the subscription.cancel from the onDone event handler. If you get a "done" event, the subscription is done, there is no need to cancel it.
I also added a string buffer to avoid the quadratic time and space complexity of repeated string concatenation.
Looking at the code, you seem to be concatenating lines right after splitting them, maybe all you need is:
response.transform(Utf8Decoder()).join("").then(completer.complete);
I'll assume for now that the splitting+joining is necessary.
In that case, what I'd actually prefer to do instead is of using listen is:
var buffer = StringBuffer();
response
.transform(Utf8Decoder())
.transform(LineSplitter())
.forEach((line) {
buffer.write(line);
}).then(() {
completer.complete(buffer.toString());
}, onError: (e, s) {
completer.completeError(e, s);
});
or, if in an async function:
try {
var buffer = StringBuffer();
await for (var line in response.transform(Utf8Decoder()).transform(LineSplitter())) {
buffer.write(line);
}
completer.complete(buffer.toString());
} catch(e, s) {
completer.completeError(e, s);
}

Waiting for Futures raised by other Futures

I'm using the Lawndart library to access browser data, and want to collect the results of a set of queries. Here's what I thought should work:
numberOfRecordsPerSection(callback) {
var map = new Map();
db_sections.keys().forEach((_key) {
db_sections.getByKey(_key).then((Map _section) {
int count = _section.length;
map[_key] = count;
});
}).then(callback(map));
}
However, when the callback is called, map is still empty (it gets populated correctly, but later, after all the Futures have completed). I assume the problem is that the Futures created by the getByKey() calls are not "captured by" the Futures created by the forEach() calls.
How can I correct my code to capture the result correctly?
the code from How do I do this jquery pattern in dart? looks very similar to yours
For each entry of _db.keys() a future is added to an array and then waited for all of them being finished by Future.wait()
Not sure if this code works (see comments on the answer on the linked question)
void fnA() {
fnB().then((_) {
// Here, all keys should have been loaded
});
}
Future fnB() {
return _db.open().then((_) {
List<Future> futures = [];
return _db.keys().forEach((String key_name) {
futures.add(_db.getByKey(key_name).then((String data) {
// do something with data
return data;
}));
}).then((_) => Future.wait(futures));
});
}

Breezejs [Q] Unhandled rejection reasons (should be empty)

These days,i have learned breezejs,durandaljs, so i made an spa application for excersizing,but breezejs(or q.js) often gives out errors
[Q] Unhandled rejection reasons (should be empty): ["proto.saveChanges#http:...s/jquery-1.9.1.js:2750\n"] (Firefox)
[Q] Unhandled rejection reasons (should be empty):(no stack) Error: Client side validation errors encountered - see the entityErrors collection on this object for more detail (IE10, but why deleteing an entity triggers validation ?)
I feel disappointed to use breezejs, what on earth am i doing!!!
I just do saving and deleting customer, sometimes error occured as above, sometimes works fine.(how confused i am feeling! :'( )
Here is part of my datacontext
var saveChanges = function () {
return manager.saveChanges()
.then(saveSuccess)
.fail(saveFailure); //.done() does not work either
//
function saveSuccess() {
console.log("Save Success!");
}
//
function saveFailure(error) {
console.log("Save Failure!");
throw error;
}
};
To save a customer:
define(['modules/dataService'], function (datacontext) {
var ctor = function () {
this.entity = ko.observable();
};
ctor.prototype.activate = function () {
//problem code --> [Q] Unhandled rejection reasons (should be empty)
//it will always create empty Customer when activate is called.
//so error occured when i switch in because of creating empty Customer every time.
this.entity(datacontext.createEntity('Customer'));
};
ctor.prototype.saveClick = function () {
if (this.entity().entityAspect.validateEntity())
datacontext.saveChanges();
else
console.log('validation error!');
};
return ctor;
});
To delete a customer
define(function (require) {
var datacontext = require('modules/dataService');
var vm = {
customers: ko.observableArray(),
activate: function () {
var self = this;
return datacontext.getCustomers(self.customers);
},
deleteCustomer: deleteCustomer
};
return vm;
//delete customer
function deleteCustomer(customer) {
vm.customers.remove(customer);
//Sets the entity to an EntityState of 'Deleted'
customer.entityAspect.setDeleted();
datacontext.saveChanges();
}
});
I think my code would work fine, but it can't!
Where is the fatal error i make? plz let me know.
Thanks in advance!
I know this thread has been here for more than a year now but I thought I could share my story.
I just got the same error while using breeze + angularJS. After some research, I figured it out:
I was passing null values in some of the entitie's properties while those fields in the database table where marked as NOT NULL.
Breeze - saveChanges
In the implementation of breeze.saveChanges, a check is done on a internal flag (line 12743 approx : if (this.validationOptions.validateOnSave) ...)
This is to enable verification of the entity against the database schema (aka the metadata).
Now most of the time we tend to call saveChanges without any parameters. And the error does not show otherwise then in the console as a general validation error message.
What have i done
We'll my fix was in 2 parts:
Add some code in my calls to saveChanges in order to trap these errors and display a better message in the console (see code below)
Fix either the DB schema (i.e. Relax NOT NULL fields to NULLABLE) OR set some default values OR enforce the business logic by adding required attributes to input controls.
Here's a snippet of the code I now use to trap the errors:
return manager.saveChanges(null, null, null, function (errors) {
console.log('breeze saveChanges returned some errors: ');
errors.entityErrors.forEach(function(e) {
console.log(e.errorMessage, e);
});
}); // return promise

indexed_db getObject() - how to return result

I would like to know how to define the data type and how to return the object (record) using getObject(). Currently, the only way that I have been able to use the result (record) outside of the function that obtains it is to call another function with the result. That way, the data-type does not need to be specified. However if I want to return the value, I need to define the data-type and I can't find what it is. I tried "dynamic" but that didn't appear to work. For example ":
fDbSelectOneClient(String sKey, Function fSuccess, String sErmes) {
try {
idb.Transaction oDbTxn = ogDb1.transaction(sgTblClient, 'readwrite');
idb.ObjectStore oDbTable = oDbTxn.objectStore(sgTblClient);
idb.Request oDbReqGet = oDbTable.getObject(sKey);
oDbReqGet.onSuccess.listen((val){
if (oDbReqGet.result == null) {
window.alert("Record $sKey was not found - $sErmes");
} else {
///////return oDbReqGet.result; /// THIS IS WHAT i WANT TO DO
fSuccess(oDbReqGet.result); /// THIS IS WHAT i'm HAVING TO DO
}});
oDbReqGet.onError.first.then((e){window.alert(
"Error reading single Client. Key = $sKey. Error = ${e}");});
} catch (oError) {
window.alert("Error attempting to read record for Client $sKey.
Error = ${oError}");
}
}
fAfterAddOrUpdateClient(oDbRec) {
/// this is one of the functions used as "fSuccess above
As someone else once said (can't remember who), once you start using an async API, everything needs to be async.
A typical "Dart" pattern to do this would be to use a Future + Completer pair (although there's nothing inherently wrong with what you've done in your question above - it's more a question of style...).
Conceptually, the fDbSelectOneClient function creates a completer object, and the function returns the completer.future. Then, when the async call completes, you call completer.complete, passing the value in.
A user of the function would call fDbSelectOneClient(...).then((result) => print(result)); to make use of the result in an async way
Your code above could be refactored as follows:
import 'dart:async'; // required for Completer
Future fDbSelectOneClient(String sKey) {
var completer = new Completer();
try {
idb.Transaction oDbTxn = ogDb1.transaction(sgTblClient, 'readwrite');
idb.ObjectStore oDbTable = oDbTxn.objectStore(sgTblClient);
idb.Request oDbReqGet = oDbTable.getObject(sKey);
oDbReqGet.onSuccess.listen((val) => completer.complete(oDbReqGet.result));
oDbReqGet.onError.first.then((err) => completer.completeError(err));
}
catch (oError) {
completer.completeError(oError);
}
return completer.future; // return the future
}
// calling code elsewhere
foo() {
var key = "Mr Blue";
fDbSelectOneClient(key)
.then((result) {
// do something with result (note, may be null)
})
..catchError((err) { // note method chaining ..
// do something with error
};
}
This future/completer pair only works for one shot (ie, if the onSuccess.listen is called multiple times, then the second time you will get a "Future already completed" error. (I've made an assumption on the basis of the function name fDbSelectOneClient that you are only expecting to select a single record.
To return a value from a single future multiple times, you'll probably have to use the new streams feature of the Future - see here for more details: http://news.dartlang.org/2012/11/introducing-new-streams-api.html
Note also, that Futures and Completers support generics, so you can strongly type the return type as follows:
// strongly typed future
Future<SomeReturnType> fDbSelectOneClient(String sKey) {
var completer = new Completer<SomeReturnType>();
completer.complete(new SomeReturnType());
}
foo() {
// strongly typed result
fDbSelectOneClient("Mr Blue").then((SomeReturnType result) => print(result));
}

Resources