Hi i am building a blockchain and am trying to sync a list of connected peers,
but if the following code is called twice from different nodes the first call is still busy while the second call kicks in does anyone know how i could wait for the first message to be complete
class Peer {
Peer(
{required this.us,
required this.peers,
required this.allPeers,
required this.myPeers});
String us;
Map<String, int> peers;
Map<String, List<Online>> allPeers;
Map<String, List<Online>> myPeers;
List<String>? keep;
Map<String, List<NewNodePeerMessage>> nnpms = {};
//listen should never trigger a response to connect we will give the address / ip
// so your ndoe two will only sync when a thrid node joins the network
Future listen() async {
ServerSocket ss =
await ServerSocket.bind(us.split(':')[0], int.parse(us.split(':')[1]));
print('listening on ${us.split(':')[1]}');
// List<ReceivePort> lrp = [];
ss.listen((client) {
// ReceivePort rp = ReceivePort();
utf8.decoder.bind(client).listen((data) async {
final PeerMessage pm =
PeerMessage.fromJson(json.decode(data) as Map<String, dynamic>);
print('recieved msg from ${pm.from}');
switch (pm.type) {
case 'new-node':
{
// rp.listen((_) async {
final NewNodePeerMessage nnpm = NewNodePeerMessage.fromJson(
json.decode(data) as Map<String, dynamic>);
print('msg${nnpm.toJson()}');
peers[pm.code] ??= 3;
allPeers[pm.code] ??= [];
final List<Online> news = [];
print('apl ${allPeers[pm.code]!.length}');
// ,maby a list would let me know
// maby we should have a simple check to the loop bool that it doesnt start looping when it is adjusting the peers
print(
'allpeers before looping ${allPeers[pm.code]!.map((e) => e.toJson()).toList()}');
for (Online one in allPeers[pm.code]!.where((element) =>
element.address != pm.from &&
element.address != us &&
!nnpm.recieved.contains(element.address))) {
print(
'i am still loopin current one ${one.toJson()} current from ${pm.from}');
try {
final Socket ones = await Socket.connect(
one.address.split(':')[0],
int.parse(one.address.split(':')[1]));
nnpm.recieved.add(us);
nnpm.recieved.add(pm.from);
// if we would only move ones out of the for loop maby the program wont work at the same point in time
// we could have a bool that keeeps track of the msg is working on printstatement you inside off the msg
// so 8787 trigger 5442 because it has him in the list
// mabe a bool can be added to list if you is inished with listening
// isbusy knows iff its stuck in the loop if we write from here we are
// if we write ffrom connect we arent or this write could know if it is busy
// is busy shoudl be from down
// because if we wirte from here or we write from you is busy is true and false
// so if we write from below can isbusy stop us
// is isusy is true herewe can go into listen but on you we can not
ones.write(json.encode(NewNodePeerMessage(
isBusy: false,
max: peers[pm.code]!,
peer: nnpm.peer,
type: 'new-node',
from: us,
code: pm.code,
recieved: nnpm.recieved)
.toJson()));
print('propablywrote ${one.toJson()} from ${nnpm.from}');
ones.listen((ppmru) async {
print('listentedtoppmru ${one.toJson()}');
final PeersPeerMessageResponse ppmr =
peersPeerMessageResponse(ppmru);
print(
'abouttoaddnewonlines ${ppmr.onlines.map((e) => e.toJson())} and from ${ppmr.from}');
// news.add(Online(online: true, address: ppmr.from));
news.addAll(ppmr.onlines);
await ones.close();
});
} catch (err) {
one.online = false;
// break;
}
print('abouttoloopagain ${one.toJson()} from ${nnpm.from}');
}
print('gothereagainactuallydonelooping ${pm.from}');
// its actually that new node only should write to the client again only if its the first time maby
allPeers[pm.code]!.addAll(news);
allPeers[pm.code]!.removeWhere((element) => !element.online);
print(allPeers[pm.code]!.map((e) => e.toJson()).toList());
/// the problem occurs because of client clients response will shut down base or we could wrap it inside o try an catch
/// we need to know if this message will send the code up here or down to printstatement you to ones.listen or to s that listen
/// one global bool could say like will go down maby even when it goes up herte to printstatement abouttoaddnewonlines
/// how do we know here below that it will go to the you printstatement or down
/// so we need a message from up
allPeers[pm.code]!.add(Online(online: true, address: nnpm.peer));
// print('abouttowriteto ${client.address.address} ${client.port}');
print('abouttorespondto ${pm.from}');
client.write(json.encode(PeersPeerMessageResponse(
isBusy: true,
peer: nnpm.peer,
onlines: allPeers[pm.code]!
.where((element) => element.address != pm.from)
.toList(),
code: pm.code,
from: us)
.toJson()));
// await client.close();
// rp.sendPort.send(null);
// });
client.destroy();
break;
}
case 'new-node-through':
{
break;
}
case 'is-online':
client.write(null);
break;
case 'is-test':
print('recieved');
client.write('irespond');
break;
default:
break;
}
// client.destroy();
}, onDone: () {});
});
}
bool loop = false;
// Future connect(List<dynamic> args) async {
// is busy shoudl bee ffrom down
Future connect(String bootnode, String code) async {
final Socket s = await Socket.connect(
"${bootnode.split(':')[0]}", int.parse(bootnode.split(':')[1]));
print('connected to ${bootnode}');
s.write(json.encode(NewNodePeerMessage(
isBusy: true,
max: 3,
peer: us,
type: 'new-node',
code: code,
from: us,
recieved: []).toJson()));
print('befforelistening');
s.listen((pmmru) async {
print('whatwas first');
PeersPeerMessageResponse ppmr = peersPeerMessageResponse(pmmru);
print(ppmr.toJson());
allPeers[ppmr.code] ??= [];
allPeers[ppmr.code]!.addAll(ppmr.onlines);
allPeers[ppmr.code]!.add(Online(online: true, address: ppmr.from));
print(allPeers);
await s.close();
}, onDone: () {
print('doschopnescheee');
});
//because off up being triggered ffrom this msg we know it will go down because off client that write
// and client that write is up their and it m
// final somekindloop;
//if we would just never listen here would it relay on up might solve problem because up might be busy we could also ssst the isolate
// s.listen((ppmru) async {
// loop = true;
// // whenever we recieve here the ppmru could have the isbusy instead
// // print('you');
// // isBusy
// // while (!loop) {}
// PeersPeerMessageResponse ppmr = peersPeerMessageResponse(ppmru);
// print(ppmr.toJson());
// allPeers[ppmr.code] ??= [];
// allPeers[ppmr.code]!.addAll(ppmr.onlines);
// allPeers[ppmr.code]!.add(Online(online: true, address: ppmr.from));
// print(allPeers);
// await s.close();
// });
}
PeersPeerMessageResponse peersPeerMessageResponse(Uint8List resp) {
final PeersPeerMessageResponse ppm = PeersPeerMessageResponse.fromJson(
json.decode(String.fromCharCodes(resp).trim()) as Map<String, dynamic>);
return ppm;
}
// Future isOnline(String code) async {
// for (Online p in allPeers[code] ??= []) {
// try {
// final Socket peer = await Socket.connect(
// p.address.split(':')[0], int.parse(p.address.split(':')[1]));
// await peer.close();
// } catch (err) {
// p.ischis = false;
// }
// }
// }
}
its about the new node function inside of the switch statement while the first call is inside off the for loop the second call does not loop but increments the allPeers with new peers which because off the first call evolves into
Unhandled exception:
Concurrent modification during iteration: Instance(length:3) of '_GrowableList'.
#0 ListIterator.moveNext (dart:_internal/iterable.dart:336:7)
#1 WhereIterator.moveNext (dart:_internal/iterable.dart:438:22)
#2 Peer.listen.<anonymous closure>.<anonymous closure> (package:gov/peer/peer.dart:180:53)
<asynchronous suspension>
how can is use the on done event to wait or the first call to be complete?
Related
I am failing to understand, why the error thrown from addItem method in below code is not caught in the try-catch block
void main() async {
var executor = Executor();
var stream = Stream.fromIterable([0, 1, 2, 3, 4, 5, 6, 7]);
try {
await for (var _ in stream) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
Future<void> demoMethod() async {
var list = [1, 2, 3, 1, 4, 5];
var executor = Executor();
var test = Test();
for (var element in list) {
executor.submit(() => test.addItem(element));
}
await executor.execute();
test.list.forEach(print);
}
class Test {
var list = <int>[];
Future<void> addItem(int i) async {
if (list.contains(i)) {
throw Exception('Item exists');
}
list.add(i);
}
}
class Executor {
final List<Future<void>> _futures = [];
bool _disposed = false;
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation());
} else {
throw Exception('Executor is already disposed');
}
}
Future<void> execute() async {
await Future.wait(_futures, eagerError: true);
_disposed = true;
}
}
but below code is able to catch the error properly
void main() async {
var executor = Executor();
try {
for (var i = 0; i < 10; i++) {
executor.submit(() => demoMethod());
}
await executor.execute();
} catch (e) {
print(e);
}
}
I am guessing it has something to do with the stream processing.
It's the stream.
In your other examples, you synchronously run through a loop a and call Executor.submit with all the computations, then immediately call executor.execute().
There is no asychronous gap between calling the function which returns a future, and Future.wait starting to wait for that future.
In the stream code, each stream events starts an asynchronous computation by calling Executor.submit. That creates a future, stores it in a list, and goes back to waiting for the stream.
If that future completes, with an error, before the stream ends and Future.wait gets called, then there is no error handler attached to the future yet. The error is then considered unhandled, and is reported to the current Zone's uncaught error handler. Here that's the root zone, which means it's a global uncaught error, which may crash your entire program.
You need to make sure the future doesn't consider its error unhandled.
The easiest way to do that is to change submit to:
void submit(Future<void> Function() computation) {
if (!_disposed) {
_futures.add(computation()..ignore());
} else {
throw StateError('Executor is already disposed');
}
}
The ..ignore() tells the future that it's OK to not have an error handler.
You know, because the code will later come back and call executor.execute, that any errors will still be reported, so it should be safe to just postpone them a little. That's what Future.ignore is for.
(Also changed Exception to StateError, because that's what you should use to report people using objects that have been disposed or otherwise decommissioned.)
This question already has answers here:
chrome.runtime.onMessage response with async await
(7 answers)
Closed 1 year ago.
I am having an issue of asynchronicity (I believe). sendResponse() in contentscript.js does not wait for getThumbnails() to return.
I am sending a message in popup.js:
chrome.tabs.sendMessage(tabs[0].id, {message: "get_thumbnails", tabUrl: tabs[0].url},
function (respThumbnails) {
const thumbUrl = respThumbnails.payload;
console.log("payload", thumbUrl)
}
);
Then, in contentscript.js I listen for this message:
chrome.runtime.onMessage.addListener(async function(request,sender,sendResponse) {
if(request.message === "get_thumbnails") {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload:payload});
}
});
async function getThumbnails() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
};
But it is also possible that the problem is coming from my getThumnails() function, because most of the times, payload is null and not undefined. So getThumbnails() might return before it is completely executed.
If this is the case, I have no idea why...
I also tried this code for getThubnails():
async function getThumbnails() {
let x = await function() {
let tUrl = null;
var potentialLocations = [
{sel: "meta[property='og:image:secure_url']", attr: "content" },
{sel: "meta[property='og:image']", attr: "content" },
];
for(s of potentialLocations) {
if(tUrl) return
const el = document.querySelector(s.sel);
if(el) {
tUrl = el.getAttribute(s.attr) || null;
}
}
return tUrl;
}
return x;
};
But this does not work, it seems to break my code...
The callback of onMessage should return a literal true value (documentation) in order to keep the internal messaging channel open so that sendResponse can work asynchronously.
Problem
Your callback is declared with async keyword, so it returns a Promise, not a literal true value. Chrome extensions API doesn't support Promise in the returned value of onMessage callback until https://crbug.com/1185241 is fixed so it's just ignored, the port is immediately closed, and the caller receives undefined in response.
Solutions
Remove the async keyword from before (request, sender, sendResponse), then...
Solution 1
Call an async function that can be embedded as an IIFE:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "get_thumbnails") {
(async () => {
const payload = await getThumbnails();
console.log("thumbPayload after function:", payload)
sendResponse({payload});
})();
return true; // keep the messaging channel open for sendResponse
}
});
Solution 2
Declare a separate async function and call it from the onMessage listener:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.message === "get_thumbnails") {
processMessage(msg).then(sendResponse);
return true; // keep the messaging channel open for sendResponse
}
});
async function processMessage(msg) {
console.log('Processing message', msg);
// .................
return 'foo';
}
I have a Dart application which receives udp datagram every 10ms. I want to know when data stream is stopped. I have a RawDatagramSocket as _udpSocket I can listen datagram by using this function:
RawDatagramSocket? _udpSocket;
Future<void> bindSocket() async {
_udpSocket = await RawDatagramSocket.bind(InternetAddress.anyIPv4, port);
setTimeout();
}
Future<void> listen() async {
_udpSocket?.listen((event) async {
Datagram? d = _udpSocket?.receive();
if (d == null) {
return;
}
//handle received data
});
}
And also I have a function to handle timeout:
void setTimeout() {
//if there is no data receiving in a couple of cycle, time out will be triggered.
//1 cycle == 10ms, I want timeout to be triggered after 10 cycles. (100ms)
_udpSocket?.timeout(timeoutDuration, onTimeout: (sink) {
//Handle time out
});
}
I am able to receive and process incoming data, but timeout function is not working.
Is there anything wrong with my code, or is there any other method to do what I want.
I figured it out,I updated the listen function. Here is the update for those who would need it:
final Duration timeoutDuration = const Duration(milliseconds: 100);
#override
Future<void> listen() async {
_udpSocket?.timeout(timeoutDuration, onTimeout: ((sink) {
//do your work when data stream closed
})).listen((event) async {
Datagram? d = _udpSocket?.receive();
if (d == null) {
return;
}
//handle received data
});
}
I hope it will be useful.
I'd like to write a multi-stage process linearly (as shown below) that starts with a file download with progress:
/// Processes the database, from download to prices processing.
Future<void> updateDatabase() async {
//final fontText = await File('./example/cosmic.flf').readAsString();
//print(art.renderFiglet('updateDatabase'));
// == 1) download
print('1 ======== DOWNLOADING ==========');
try {
await startDownloading();
} catch (err) {}
// == 2) Decompress: Whatever download was ok or not, we decompress the last downloaded zip file we have locally
print('2 ======== DECOMPRESSING ========');
try {
await startDecompressing();
} catch (err) {}
// == i) Stage i, etc.
But something does not work in my download stage as it starts stage 2) prior stage 1) completion.
My stage one (download) is like so:
/// Starts download procress
Future<void> startDownloading() async {
print("startDownloading…");
_state = DownloadState.downloading;
_progress = 0;
notifyListeners();
/// Database string url
final databaseUrlForInstantData = "https://XXX";
try {
final request = Request('GET', Uri.parse(databaseUrlForInstantData));
final StreamedResponse response = await Client().send(request);
final contentLength = response.contentLength;
// == Start from zero
_progress = 0;
notifyListeners();
/// The currently downloaded file as an array of bytes
List<int> bytes = [];
response.stream.listen(
/// = Closure listener for newly downloaded bytes
(List<int> newBytes) {
bytes.addAll(newBytes);
final downloadedLength = bytes.length;
if (contentLength == null) {
_progress = 0;
} else {
_progress = downloadedLength / contentLength;
}
notifyListeners();
print(
'Download in progress $_progress%: ${bytes.length} bytes so far');
},
/// = Download successfully completed
onDone: () async {
_state = DownloadState.downloaded;
notifyListeners();
/// The resulting local copy of the database
final file = await _getDownloadedZipFile();
// == Write to file
await file.writeAsBytes(bytes);
print('Download complete: ${bytes.length} bytes');
},
/// = Download error
onError: (e) {
_state = DownloadState.error;
_error = e.message;
print('Download error at $_progress%: $e');
},
cancelOnError: true,
);
}
// == Catches potential error
catch (e) {
_state = DownloadState.error;
_error = 'Could not download the databse: $e';
print('Download error at $_progress%: $e');
}
}
Your startDownloading function returns after it registers callbacks to listen to the Stream and does not wait for the Stream to complete.
To wait for the Stream to complete, you can save the StreamSubscription returned by .listen and then await the Future from StreamSubscription.asFuture:
var streamSubscription = response.stream.listen(...);
await streamSubscription.asFuture();
I'm sending a data to the server like this:
save(){
var el = this.parent.nodes;
print(el);print(el.length);
request = new HttpRequest();
if(el.length == 1) print('No lines to save!');
else
{
var opt = el[i].shadowRoot.nodes[0].options[el[i].shadowRoot.nodes[0].selectedIndex].text;
print(this.parent.nodes.length);
for(var i=1; i < el.length; i++)
{
orderLines.add({
'poid': orderHeader[0]['OrderID'],
'ponum': orderHeader[0]['onum'],
'lnum' : i.toString(),
'itmid' :el[i].shadowRoot.nodes[0].value,
'icode' : opt,
'qty': el[i].shadowRoot.nodes[1].value,
'rqty': 0,
'bqty': el[i].shadowRoot.nodes[1].value,
'iqty': 0,
'biqty': el[i].shadowRoot.nodes[1].value,
'price': el[i].shadowRoot.nodes[2].value,
'rdd': orderHeader[0]['rdd'],
'eta': '',
'flag': 0
});
print(orderLines);
request.onReadyStateChange.listen(onData_save);
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
}
}
}
and my server side function is:
void main() {
connections = new List<WebSocket>();
HttpServer.bind(HOST, PORT).then((HttpServer server) {
print('Server listening on port ${PORT}.');
server.listen((HttpRequest request) {
if (WebSocketTransformer.isUpgradeRequest(request)) {
WebSocketTransformer.upgrade(request).then(handleWS);
} else gotMSG(request);
});
});
}
handleWS(WebSocket ws){
connections.add(ws);
print('Client connected, there are now ${connections.length} client(s) connected.');
ws.listen((String message) {
for (WebSocket connection in connections) {
connection.add(message);
}
},
onDone: () {
connections.remove(ws);
print('Client disconnected, there are now ${connections.length} client(s) connected.');
});
}
void gotMSG(HttpRequest request) {
switch (request.method) {
case 'POST':
handlePost(request);
break;
case 'OPTIONS':
handleOptions(request);
break;
default:
defaultHandler(request);
}
}
void serveRequest(HttpRequest request){
print('Listening for GET and POST on http://$HOST:$PORT');
request.response.statusCode = HttpStatus.FORBIDDEN;
request.response.reasonPhrase = "WebSocket connections only";
request.response.close();
}
void handlePost(HttpRequest req) {
HttpResponse res = req.response;
switch (req.uri.path) {
case '/login': login(req); break;
...
case '/sPO': savePO(req); break;
default: break;
}
}
The /sPO => savePO is executed once if the order sent is of one line only, but if n lines in the order, the function is executed more than once, could not find a pattern for that,
In the SavePO I used oracledart pub, so thought something wrong in it, and tried postgresql pub, but got same results, the savePO function is:
void savePO(HttpRequest req){
HttpResponse res = req.response;
addCorsHeaders(res);
print('${req.method}: ${req.uri.path}');
Future future() => new Future.value(true);
req.listen((List<int> buffer) {
var theDataLines = JSON.decode(new String.fromCharCodes(buffer));
print(theDataLines);
connect(db).then((conn) {
for (var theData in theDataLines)
conn.execute("""
insert into pol
(poid,ponum,lnum,itmid,icode,qty,rqty,bqty,iqty,biqty,price,rdd, eta, flag)
values (#poid,#ponum,#lnum,#itmid,#icode,#qty,#rqty,#bqty,#iqty,#biqty,#price,
to_timestamp(#rdd,'YYYY-MM-DD'), to_timestamp(#eta,'YYYY-MM-DD'), #flag)
""",
{
'poid': theData['poid'],
'ponum': theData['ponum'],
'lnum' : theData['lnum'],
'itmid' : theData['itmid'],
'icode' : theData['icode'],
'qty': theData['qty'],
'rqty': theData['rqty'],
'bqty': theData['bqty'],
'iqty': theData['iqty'],
'biqty': theData['biqty'],
'price': theData['price'],
'rdd': theData['rdd'].toString(),
'eta': theData['eta'].toString(),
'flag': theData['flag']
})
.then((_)=>conn.query('commit').toList().then((rows) {print('committed');}))
.then((_){
res.write('done');
res.close();
});
}); // END of SQL
}, onError: printError); // End of server listen
} // END of function
I even tried to change the:
case '/sPO': savePO(req); break;
to be
case '/sPO': print(1); break;
the it printed the 1, 4 times after sending an order of 6 lines!!
It's hard to see for me what you actually try to accomplish.
The problem is very probably your save() method. You wrote how it behaves but not much about what you try to accomplish?
Why don't you put more lines into one JSON string and post them together in one request?
You create one request instance and call send repeatedly on this one request instance.
You also register the onReadyStateChange handler more than once on the same request object which results in onData_save being called several times when the event occurs just once.
I think you should either move request = new HttpRequest(); down just before
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
or better move request.onReadyStateChange.listen(onData_save); up to request = new HttpRequest();,
add all orderlines into one JSON and call
request.open('POST', host+'/sPO');
request.send(JSON.encode(orderLines));
after the for loop.
Another problem I see is that you do a fire and forget. What if the send request fails for some reason?
I would create a sendJSON method that returns a future (with a Completer which completes on onDone and completeError when something goes wrong.
When you want to create more than one request in your save() you can use something like
// create your JSON
var futures = [];
for(i = 0; i < 5; i++) {
futures.add(sendData(myJson)); // collects the futures returned from sendData
}
// executes all futures and waits for all to respond and then returns another future
return Future.wait()
.then((results) {
results.forEach((r) {
// check result
});
});