2
votes

So I've been working on experimenting with DART (whereby my core languages are C++, and embedded C derivates). Hence my code is probably not pretty as I'm more of a procedural programmer, but I'm getting by and learning... I've been struggling around Futures pertaining to await sync, and basically, I simply can't get DART to WAIT. The following code establishes a socket connection to a small embedded device and extracts info. That all works, but notice the order of operations SHOULD be main() gets some info from the console, then should call the method cardStatus to run off and get the info from the embedded device via the socket connection. This is where the await should occur. When the Future is returned, it should go off to the printstuff() method. I've added print statements that should go in order and read:

  • This should print 1st
  • This should print 2nd
  • This should print 3rd

Instead since the wait is not occurring on the cardstatus call (which is time consuming), I get:

  • This should print 1st
  • This should print 3rd
  • This should print 2nd

I've followed another thread on the use of async, and seem to be at least following one solid way of using this Other thread (I tried a .then with a completer with a similar result, so there is something core I feel I'm missing).. but I've been stuck on this for a week.

Code below, along with the console output.

import 'dart:io';
import 'dart:async' show Future;

const String STATUS = "#111111;";

String defaultIP = "10.1.14.202";
int defaultConfigPort = 5111;
int defaultControlPort = 6722;

var card = new Map();

getInput(String defaults) {
  String takenin = stdin.readLineSync();
  if (takenin == '') takenin = defaults;
  return takenin;
}

Future main() async {
  stdout.write('What is the IP address of the card ($defaultIP): ');
  String ipaddress = getInput(defaultIP);
  defaultIP = ipaddress;
  print ("This should print 1st");
  stdout.writeln("Connecting to $defaultIP");
  await cardStatus(defaultIP, defaultConfigPort, STATUS, card);
  printstuff();
}

printstuff() {
  stdout.writeln(card['subnet']);
  print ("This should print 3rd");
}
Future cardStatus(String ip, int port, String message, Map card) {
  return new Future.delayed(Duration.ZERO, () {
    Socket.connect(ip, port).then((socket) {
      print('Connected to: '
          '${socket.remoteAddress.address}:${socket.remotePort}');

      socket.listen((data) {
        print(new String.fromCharCodes(data).trim());
        List str1 = (new String.fromCharCodes(data).trim().split(','));
        print(str1);
        print ("This should print 2nd");
        //var card = new Map();
        card['ip'] = str1[0];
        card['subnet'] = str1[1];
        card['gateway'] = str1[2];
        card['unknown'] = str1[3];
        card['persist'] = str1[4] == 'true';
        card['build'] = str1[5];
        card['serial'] = str1[6].substring(0, 14);
        card['cloudpassword'] = str1[6].substring(14, 20);
        card['DNS'] = str1[7];
        card['cloudhost'] = str1[8];
        card['cloudenabled'] = str1[9] == 'true';
        print(card['ip']);
      },
          onDone: () {
            print("Done");
            socket.destroy();
          });

//Send the request
      socket.write(message);
    });
  });
}

and this is the current console output. notice the null shouldn't be a null if the cardStatus would have completed it would be printed str1.

What is the IP address of the card (10.1.14.202): 
This should print 1st
Connecting to 10.1.14.202
null
This should print 3rd
Connected to: 10.1.14.202:5111
>10.1.14.202,255.255.255.0,10.1.14.1,,0,435,F44900A60040F8000000,192.168.1.1,connect.tutuuu.com,0;
[>10.1.14.202, 255.255.255.0, 10.1.14.1, , 0, 435, F44900A60040F8000000, 192.168.1.1, connect.tutuuu.com, 0;]
This should print 2nd
10.1.14.202
Done

Process finished with exit code 0

Thanks for all the help!

2
Please post a simpler example in the future, most of it is not interesting to your problem.rkj
Not a problem. Sorry about that.mumcs01

2 Answers

6
votes

You are missing return before Socket.connect. As it stands now your code just starts connecting but never awaits it through future. I would highly recommend using as much as possible the new await / async syntax.

Here is a running example that does get google homepage:

import 'dart:io';
import 'dart:async' show Future;

Future main() async {
  print("This should print 1st");
  await cardStatus('www.google.com', 80, 'GET /\nHTTP 1.1\n\n');
  printstuff();
}

printstuff() {
  print("This should print 3rd");
}

Future cardStatus(String ip, int port, String message) {
  return new Future.delayed(Duration.ZERO, () {
    return Socket.connect(ip, port).then((socket) {
      print('Connected to: '
          '${socket.remoteAddress.address}:${socket.remotePort}');

      socket.listen((data) {
        List str1 = (new String.fromCharCodes(data).trim().split(','));
        print(str1.first);
        print("This should print 2nd");
      }, onDone: () {
        print("Done");
        socket.destroy();
      }, onError: (e) {
        print("Error while listening: $e");
      });
      socket.write(message);
    });
  });
}

Below slightly redacted version that uses awaits, and try / catch to handle errors:

import 'dart:io';
import 'dart:async' show Future;

Future main() async {
  print("This should print 1st");
  await cardStatus('www.google.com', 80, 'GET /\nHTTP 1.1\n\n');
  print("This should print 3rd");
}

Future<String> cardStatus(String ip, int port, String message) async {
  var socket = await Socket.connect(ip, port);
  print('Connected to: '
      '${socket.remoteAddress.address}:${socket.remotePort}');
  socket.write(message);
  print("Sent request");
  try {
    var response = await socket.fold(
        '',
        (String acc, List<int> data) =>
            acc + new String.fromCharCodes(data).trim());
    print("Received response: ${response.substring(0, 10)}");
    return response;
  } finally {
    socket.close();
  }
}
2
votes

I know it was answered but the question is great and I struggled myself with the concept so here is another element that got me to understand. in the dartpad (https://dartpad.dartlang.org/) try this (comments are in the code):

import 'dart:async';

//Just creating a duration to use later
Duration duration = new Duration(milliseconds: 500);

void main() {
  //This is what tricked me, printStuff is async so running in parallel processing by default
  //There is no need to call the function in a certain way (like a go xxx for a goroutine)
  //there is an await in the function so it will wait inside the function only 
  //i.e. printStuff('a') starts then printStuff('b') starts straight away...already in prallel processing
  //Run it and check the output 
  printStuff('a');
  printStuff('b');
  //Basically the await is in the function so printStuff is still returning a Future
  //i.e. printStuff('a') starts but doesn't wait to complete to start printStuff('b')   
}
Future<void> printStuff(String id) async {
  for(int i = 0; i <= 5; ++i) {
    //this await is waiting for the command to complete to move to the next iteration...
    //the i iterations are done one after the other
    await new Future.delayed(duration, () {
      print(id + i.toString()); 
    });   
  }    
}

Then try this:

import 'dart:async';

Duration duration = new Duration(milliseconds: 500);

//becuase I use await in main now, I must make it return a future and be async
Future main() async {
  //to make it happen one after the other, you need await at a call function level
  await printStuff('a');
  await printStuff('b');
  //Basically this says complete printStuff('a'), then start printStuff('b')...
  //and yes technically one doesn't need the second await becuase there is nothing after
}

Future<void> printStuff(String id) async {
  for(int i = 0; i <= 5; ++i) {
    await new Future.delayed(duration, () {
      print(id + i.toString());
    });
  }
}

So my personal misunderstanding was that an async function is called in parallel straight away and an await in a function waits for real but in the function itself, with no impact on other parallel processing happening.