The sync version blocks the main thread. The callback and promise versions do the work in the background. Sync is acceptable only when nothing else needs to run yet, like at startup.
The three APIs:
fs.readFileSyncblocks the main thread until the operation finishes. It throws an error if it fails.fs.readFile(path, callback)runs the work on the libuv thread pool. The thread pool is a small set of background threads that Node uses for slow tasks like disk access. When the work is done, Node calls an error-first callback, meaning the first argument is the error ornull.fs/promises(require('node:fs/promises')orfs.promises) does the same work but returns promises. So it works withasync/awaitandtry/catch.
In a server, use the async forms in any code that runs per request. Why? A sync call stops every other client while it waits for the disk.
Sync calls are fine where nothing else needs to run yet:
- loading configuration or certificates at startup
- CLI scripts
- build tools
In modern code, the promise API is the usual default.
- Sync calls block the event loop and throw on error.
- The callback and promise APIs run the work on the thread pool.
fs/promisesworks withasync/await.- Never use sync calls in request handlers.
- Sync calls are fine at startup and in scripts.
Read more