Java는 멀티스레드 기반이라 Javascript의 이벤트 루프 기반 동기, 비동기 동작 양상과는 다른 듯 하다. Javascript는 싱글 스레드 기반 언어라서 비동기적으로 작업을 수행하기 위해서 이벤트 루프라는 개념을 활용한다고 함!
Java에서 어떤 스레드 내의 동기 코드들은 그들간의 실행 순서가 보장된다.
그런데 어떤 함수 내에서 비동기 코드 (CompletableFuture.runAsync()) 를 호출했을 때는, 이 코드가 별도의 스레드에서 실행되기 때문에, 함수 내의 다른 코드 라인들과 비동기로 작동해서, 함수 내 다른 코드들과 비동기 코드는 서로 실행 순서가 코드 순서대로 보장이 안될 수 있다. 그 타이밍은 스레드 스케줄러가 어떤 알고리즘에 따라 어떻게 CPU 자원을 할당할 스레드를 결정하느냐에 따라 달라질 듯 하다.
배열 객체가 가지는 foreach 메소드는, 콜백 함수로 내부에 await 코드 라인을 갖고 있는 async 함수를 넘기면, foreach의 동작 자체의 순서가 보장이 안될 수도 있다. 특정 순회에서 await에 걸렸을 때, 다른 단계의 순회를 비동기적으로 시작시켜버릴 수 있기 때문이다.
이와는 다르게 for...of 를 쓰면, 다른 작업과의 비동기 작업을 목적으로 내부에 await을 지정한 코드 라인을 쓰더라도, 이 함수 외의 다른 작업과 비동기 작업을 수행하되, for...of에서 수행하는 순회들 간의 실행 순서는 보장이 된다.
그래서 배열에 대해 각 원소를 순회하며 순차적으로 어떤 함수를 실행시키고 싶을 때, 이 순회 작업들의 순서를 보장하면서 다른 작업과 비동기적으로 수행하고 싶다면 for...of 를 쓰는게 맞는 것 같다.
Java는 멀티스레드 환경이라 기본적으로 어떤 함수 내에서 비동기 코드를 썼을 때, 그 코드와 다른 코드들 간의 실행 순서를 보장하기 어려운 듯하다. 그래서 그걸 가능하게 하는 방법은 future.join()을 통해 비동기 작업들이 모두 완료되기를 기다린 후, 그 다음의 코드들을 실행하는 것인듯하다.
만약 비동기 작업들간의 실행 순서를 보장하고 싶다면, 비동기 코드를 각각 실행해서 별도의 스레드들을 만들어서 병렬 수행하면 보장이 어려우니, 선행 작업에 해당하는 작업을 비동기로 수행하고, thenRun() 등을 활용해서 후행 작업을 체이닝해서 비동기 작업 간의 실행 순서를 보장해야하는 듯하다.