8

There are a number of sync and async operations for files in dart:io:

  • file.deleteSync() and file.delete()
  • file.readAsStringSync() and file.readAsString()
  • file.writeAsBytesSync(bytes) and file.writeAsBytes(bytes)
  • and many, many more.

What are the considerations that I should keep in mind when choosing between the sync and async options? I seem to recall seeing somewhere that the sync option is faster if you have to wait for it to finish anyway (await file.delete() for example). But I can't remember where I saw that or if it is true.

Is there any difference between this method:

Future deleteFile(File file) async {
  await file.delete();
  print('deleted');
}

and this method:

Future deleteFile(File file) async {
  file.deleteSync();
  print('deleted');
}
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • use `sync` variants only in cases where you are sure it will finish in couple of milliseconds, not longer – pskink Apr 12 '20 at 09:57
  • @pskink, Even if I am in an async method? – Suragch Apr 12 '20 at 10:02
  • yes, even in `async` method, btw `File` docs say: *"Most methods in this class occur in synchronous and asynchronous pairs, for example, readAsString and readAsStringSync. Unless you have a specific reason for using the synchronous version of a method, prefer the asynchronous version to avoid blocking your program."* – pskink Apr 12 '20 at 10:17
  • @pskink, I can certainly go with that, though on a deeper level I'd still like to know why. If I'm in an `async` method, then running the synchronous version isn't going to block the program any more than using `await` plus the asynchronous version would. Am I wrong about that? – Suragch Apr 12 '20 at 10:42
  • well, try to read some half GB mp4 file and you will see... ;-) – pskink Apr 12 '20 at 10:53
  • @pskink Really? Would `file.readAsBytesSync()` for a large file block the program if it is in an async method? That's interesting. That fundamentally changes how I viewed async methods. I previously thought that anything that happens in an async method essentially runs on a different isolate. – Suragch Apr 12 '20 at 11:00
  • 1
    yes, of course, do you think that you could create `for(var i=0; i<99999999999999999999999999999;i++)` and that loop would somehow work without blocking your app? in dart, by default, everything works in one thread - https://medium.com/@parthdave93/single-thread-dart-what-part-2-a5592bef5213 – pskink Apr 12 '20 at 11:02
  • 1
    and part 1 of course: https://medium.com/@parthdave93/single-thread-dart-what-ccbca2543ae9 – pskink Apr 12 '20 at 11:10
  • @pskink, Haha, yes, I did think that. And I just tried it and you were right (of course). It blocked my app. I still have some learning to do about isolates, threads in Dart, and async methods. Thanks for your help. – Suragch Apr 12 '20 at 11:15
  • sure, your welcome, so you tried to run `readAsBytesSync` on a huge file? – pskink Apr 12 '20 at 11:16
  • @pskink no, I did a huge for loop. – Suragch Apr 12 '20 at 11:17
  • 1
    aha, btw for using `Isolate`s there is a nice class: `IsolateChannel` which gives you a `Stream` / `Sink` 2 way communication channel with `Isolate` - that way you can for example use `Sink.add` to submit new task for heavy processing and `Isolate` could return a result via `Stream` that you can `listen()` to – pskink Apr 12 '20 at 11:19
  • I believe that general rules for asynchronous I/O apply. If you're going to perform a lot of I/O operations, using asynchronous I/O allows them to be batched together and should give better performance overall. Making things a bit confusing is that there is an [`avoid_slow_async_io`](https://dart-lang.github.io/linter/lints/avoid_slow_async_io.html) Dart lint. – jamesdlin Apr 12 '20 at 19:30
  • @jamesdlin Ah, yes, that's where I had seen that! Thank you. It would be nice to see an in depth explanation of the reason behind the `avoid_slow_async_io` lint some time. – Suragch Apr 13 '20 at 00:44
  • @Suragch You perhaps could comment on https://github.com/dart-lang/sdk/issues/36269 and ask for clarification. – jamesdlin Apr 13 '20 at 01:11

2 Answers2

13

Let me try to summarize an answer based on the comments to my question. Correct me where I'm wrong.

  • Running code in an async method doesn't make it run on another thread.
  • Dart is a single threaded system.
  • Code gets run on an event loop.
  • Performing long running synchronous tasks will block the system whether it is in an async method or not.
  • An isolate is a single thread.
  • If you want to run tasks on another thread then you need to run it on another isolate.
  • Starting another isolate is called spawning the isolate.
  • There are a few options for running tasks on another isolate including compute and IsolateChannel and writing your own isolate communication code.
  • For File IO, the synchronous versions are faster than the asynchronous versions.
  • For heavy File IO, prefer the asynchronous version because they work on a separate thread.
  • For light File IO (like file.exists()?), using the synchronous version is an option since it is likely to be fast.

Further reading

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
2

sync variants unlike async ones stop the CPU from executing any event handlers - like the event loop, until the operation is complete.

  • Using sync:

    void main() {
      final file = File('...');
      Future(() => print('1')); // Adding to the event queue
      file.readAsBytesSync();
      print('2');
    }
    

    Output:

    2
    1
    
  • Using async:

    void main() async {
      final file = File('...');
      Future(() => print('1')); // Adding to the event queue
      await file.readAsBytes();
      print('2');
    }
    

    Output:

    1
    2
    
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • While I agree with your explanation, it may not be obvious to some readers that `print('2')` in the async example is also added to the event queue due to the fact that it occurs after the `await` in the function. That is, it's like nesting it in a `file.readAsBytes().then` callback. – Suragch Nov 07 '22 at 06:31