So the answer is a bit subtle here. For this exact case, you will end up with arrayOne having the same items in the same order as arrayTwo, but this is more of a side effect of your sample code not being truly asynchronous and async.forEach not doing anything overly unexpected/clever. However, to make this truly reliable, you want to use an async function that guarantees order, which the letter of the contract of .forEach/.each does not. .each only guarantees that the worker function will get called once with each item in arrayTwo. It does not guarantee that it will start them all in order or wait for one to finish before starting the next, etc. If the author of async decided to start the worker functions in reverse order for some performance optimization, for example, that would change your side effects. So it's best not to rely on any behavior beyond what is explicitly guaranteed in the definition/docs for the async function. So, using something like async.series would give you a reliable way to do this and not get unexpected order in arrayOne at the end. Of course, the parallelism characteristics are now vastly different.
However, on a technical note, non of this involves the concept of "thread safety" as node only has a single execution thread. However, you end up with the same type of race conditions to think about when dealing with async code like this, so at the end of the day you can code race conditions in node just like you could in a multithreaded language.
The most helpful analogy I keep in mind is async code is ordering food by delivery. If you call 3 restaurants and order one pizza from each, that does not imply what order the deliveries arrive at your door.