Does that mean that asynchronous tasks are executed separately by libuv running a separate JS tread so this causes the above example not to crash
Not a JavaScript thread, no. Node.js runs I/O tasks on a separate, non-JavaScript thread. It also or threads — but more importantly, it uses asynchronous I/O features of the OS, where possible, to avoid even tying up that other thread / those threads up unnecessarily. It processesThose features let it queue and process I/O completionsprogress/completions on that/those other thread(s), and queuesthen queue calls to the async function's callback (your write callback, in this case) where appropriate on the main JavaScript thread's event loop.
So what you have in your code is a race between threads that are not coordinated, which explains why sometimes it works and other times it fails. Thread scheduling can seem non-deterministic because of all the factors involved at the OS level.