Rules / Suspicious / avoid-passing-async-when-sync-expected

avoid-passing-async-when-sync-expected

lint/suspicious/avoid-passing-async-when-sync-expected
recommended
Same as avoid-passing-async-when-sync-expected · Dart Code Metrics

Flags an async function literal passed where a synchronous callback is expected.

When a parameter's type is a function that does not return a Future, passing an async closure hands back a Future the caller never awaits: the body runs out of order, its exceptions escape as unhandled errors rather than being caught, and any value it produces is silently lost. Pass a synchronous closure, or widen the parameter to a Future-returning function if asynchronous work is genuinely intended. Detection matches positional call arguments against the parameter types of functions and methods declared in the same file.

Invalid

example.dartdart
// Bad: passing async function to sync parameter
void processSync(void Function() callback) {
  callback();
}

void example() {
  processSync(() async {
    await Future.delayed(Duration(seconds: 1));
    print('Done');
  });
}

class Handler {
  void setup(void Function(String) handler) {
    handler('test');
  }

  void badSetup() {
    setup((value) async {
      await processAsync(value);
    });
  }
}

void executeCallback(String Function(int) fn) {
  print(fn(42));
}

void misuse() {
  executeCallback((n) async {
    await delay();
    return '$n';
  });
}

// More violations
void performAction(void Function(String, int) callback) {
  callback('test', 42);
}

void badAction() {
  performAction((name, count) async {
    await Future.delayed(Duration(milliseconds: 100));
    print('$name: $count');
  });
}

class DataLoader {
  void loadData(String Function() fetcher) {
    final data = fetcher();
    print(data);
  }

  void loadBad() {
    loadData(() async {
      await Future.delayed(Duration(seconds: 1));
      return 'data';
    });
  }
}
Avoid passing an async function where a synchronous callback is expected
6void example() {
7 processSync(() async {
Avoid passing an async function where a synchronous callback is expected
18 void badSetup() {
19 setup((value) async {
Avoid passing an async function where a synchronous callback is expected
29void misuse() {
30 executeCallback((n) async {
Avoid passing an async function where a synchronous callback is expected
41void badAction() {
42 performAction((name, count) async {
Avoid passing an async function where a synchronous callback is expected
54 void loadBad() {
55 loadData(() async {

Valid

example.dartdart
// Good: passing sync function to sync parameter
void processSync(void Function() callback) {
  callback();
}

void example() {
  processSync(() {
    print('Done');
  });
}

class Handler {
  void setup(void Function(String) handler) {
    handler('test');
  }

  void goodSetup() {
    setup((value) {
      processSync(value);
    });
  }
}

void executeCallback(String Function(int) fn) {
  print(fn(42));
}

void goodUse() {
  executeCallback((n) {
    return '$n';
  });
}

// OK: async callback passed to async parameter
void processAsync(Future<void> Function() callback) async {
  await callback();
}

void asyncToAsync() {
  processAsync(() async {
    await Future.delayed(Duration(seconds: 1));
    print('Done');
  });
}

// OK: extracting async logic separately
void separated(void Function() callback) {
  callback();
}

void _asyncWork() async {
  await Future.delayed(Duration(seconds: 1));
}

void extract() {
  separated(() {
    _asyncWork(); // fire and forget (or capture result)
  });
}

// Good: passing sync callbacks to sync parameters
void performAction(void Function(String, int) callback) {
  callback('test', 42);
}

void goodAction() {
  performAction((name, count) {
    print('$name: $count');
  });
}

// Good: using named function for sync callback
class DataLoader {
  String _fetchData() {
    return 'data';
  }

  void loadData(String Function() fetcher) {
    final data = fetcher();
    print(data);
  }

  void loadGood() {
    loadData(_fetchData);
  }
}

// Good: sync arrow function
void processItems(void Function(String) handler) {
  handler('item');
}

void goodProcess() {
  processItems((item) => print('Processing: $item'));
}

How to configure

Set the severity of avoid-passing-async-when-sync-expected in your falcon.json:

falcon.jsonjson
{
  "linter": {
    "rules": {
      "suspicious": {
        "avoid-passing-async-when-sync-expected": "error"
      }
    }
  }
}