0

I'm still learning Dart and OOP and can't quite get this right. The problem is that _batch.addAll() executes before _invDetails is populated from the inner ForEach because of the delay from the query. How do I structure this properly?

Future<Response> exportAccpac() async {
    final Map<String, dynamic> _inv = {'inv': []};
    final Map<String, dynamic> _invDetails = {'invDet': []};
    final Map<String, dynamic> _batch = {};

    final _qExpenses = Query<UserCompany>(context);
    _qExpenses.where((e) => e.company.id).equalTo(companyId);
    _qExpenses
        .join(object: (e) => e.user)
        .join(set: (u) => u.expense)
        .where((d) => d.date)
        .lessThanEqualTo(date);

    final _expenseList = await _qExpenses.fetch();

    _expenseList.forEach((e) {
      _inv['inv'].add(
        {
         // a bunch of stuff here using e
        },
      );

      e.user.expense.forEach((userExpense) async {

        final _qGlCode = Query<GlCode>(context)
          ..where((g) => g.user_type.id).equalTo(_userTypeId)
          ..where((g) => g.company.id).equalTo(companyId)
          ..where((g) => g.expense_type.id).equalTo(userExpense.expense_type.id);

        final _glCodes = await _qGlCode.fetch();

        _invDetails['invDet'].add({
        // a bunch of stuff here using glCodes
        });
      });
    });

    _batch..addAll(_inv)..addAll(_invDetails);
  }

2 Answers 2

1

Generally avoid the use of forEach - especially with async. Restructure the code as follows:

  for (var userExpense in e.user.expense) {
    final _qGlCode = Query<GlCode>(context)
      ..where((g) => g.user_type.id).equalTo(_userTypeId)
      ..where((g) => g.company.id).equalTo(companyId)
      ..where((g) => g.expense_type.id).equalTo(userExpense.expense_type.id);

    final _glCodes = await _qGlCode.fetch();

    _invDetails['invDet'].add({
    // a bunch of stuff here using glCodes
    });
  }

This has the effect of performing the GL code queries serially, whereas previously you were kicking them off in parallel but with no way to wait for them to each finish. (To do that you'd have used Future.wait, but that shouldn't be needed here.)

You can replace the outer forEach with a for too.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the reply. The issue here though is that, since we are awaiting the query (which returns a future) the for loop needs to be async which is not possible. So, unless I'm missing something, this solution won't work either.
I've played around with this dropping await and using .then() and made some progress, but I'm still getting a problem with the outer loop completing before the inner loop completes. I'll keep trying. Also, I'm interested in why you say to generally avoid forEach since they both seem to do the same thing.
No. They don't do the same thing. foreach takes a closure, which, if it is async, returns a future. So the closure code will almost certainly execute in a later event loop. The other common mistake people make with foreach is to put return statements in them, expecting them to break from the outer loop, whereas that just returns from the closure. Hence the advise to avoid.
If also avoid then, as await code is generally more readable. To back to wait, add some more print statements and update the question with the results so far.
Thanks for the help. I got it working once I replaced the outer forEach with a for loop (as you said). I didn't realize there such a difference between the two. Learning a lot.
0

you could put your forEach under an async function and return _inv to eventually become like this:

final _expenseList = await _qExpenses.fetch();
final _inv = await _yourNewFunction();
_batch..addAll(_inv)..addAll(_invDetails);

1 Comment

Thanks. I'm pretty new to this so still not clear. What would _yourNewFunction() look like? How you fit it into the code above?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.