Javascript

반복문 for와 forEach (feat. 반복문의 비동기처리)

마손리 2022. 11. 28. 09:09

알고리즘을 배우기 시작하면서 반복문중 for와 forEach의 속도차이를 찾아보다 문득 둘의 차이점이 궁금해젔다.

 

for와 forEach의 차이점

const  arr = [1, 2, 3, 4, 5]

for (let i = 0; i < arr.length; i++){
    console.log(arr[i])
}

arr.forEach((i) => {
    console.log(i)
})

결과값

위의 두 결과값은 같다, 그렇다면 둘의 차이점은???

찾아보니 for문은 동기식, forEach는 비동기식 처리를 하며 for문을 돌리는중 에러발생시 즉각 코드를 중단한다... 란다.
 
그래서 바로 실험 시작!
 
for (let i = 0; i < arr.length; i++){
    if (i === 3) {
        const sample = 1
        sample = 2
        continue
    }
    console.log(arr[i])
} 

arr.forEach((i) => {
    if (i === 3) {
        const sample = 1
        sample = 2
    }
    console.log(i)
})

검색대로라면 위의 말도안되는 코드를 돌렸을경우 for문의 결과값은 1,2,3 그리고 forEach의 경우 1,2,4,5가 나와야 정상.

for문 결과값
forEach 결과값

잉? 뭔가 예상한 것과는 다른 결과값이 나와부럿다... 분명 forEach의 결과값은 1,2,4,5가 나와야되는데...

더 정확한 실험 결과를 얻기위해 실제 api처럼 딜레이를 만든뒤 다시 실험!!

 

const delay = () => {
    const randomDelay = Math.floor(Math.random() * 4) * 100
    return new Promise(resolve => setTimeout(resolve, randomDelay))
}

위 처럼 랜덤한 시간의 딜레이를 만들어 준뒤,

 

for (let i = 0; i < arr.length; i++) {
    if (i === 3) {
        const sample = 1
        sample = 2
    }
    delay().then(() => console.log(arr[i]))
} 

arr.forEach( i => {
   delay()
       .then(() => {
           if (i === 3) {
            const sample = 0
            sample = 1 
          }
          console.log(i)
      })
})

똑같이 말도안되는 코드를 조건문으로 달고 랜덤한 딜레이 이후 출력되도록 설정

for문 결과값
forEach 결과값

처음 예상한 것과 같이 for문은 조건문이 돌기 전의 결과값만을 출력, forEach는 에러가 나는 조건문의 결과값만을 제외하고 출력이 되었다.

 

그래서.... 왜 다른거지???

더 찾아보니 forEach의 경우 반복문을 callback 함수로 반환한다고 한다.

실제로 Postgres SQL로 API를 구축하면서 겪었던 문제였다.

 

module.exports = {
    query: (text, params, callback) => {
      const start = Date.now()
      return pool.query(text, params, (err, res) => {
        const duration = Date.now() - start
        console.log('executed query', { text, duration, rows: res.rowCount })
        callback(err, res)
      })
    },
  }

실제 작성했던 코드와 비슷한 예제로 위와 같이 query를 연결한 후 import해서 사용하려했는데

외부함수를 비동기화 하더라도 내부함수인 callback까지는 기다려주지 않았다.

(결국 callback 함수를 없애고 메인함수에서 바로 리턴값을 받아와 해결)

 

그렇다면 반복문을 순차적으로 처리할 수 있을까?

const result = async (arr) => {
    for (let i = 0; i < arr.length; i++) {
    console.log(arr[i],"start")
   await delay().then(()=>console.log(arr[i],"delayed"))
}
}
result(arr)

결과값

async 함수안에 for문을 돌리고 await으로 결과값을 출력해주면 위와 같이 이전 반복문이 끝나기를 기다린후

다음 반복문이 실행된다. (forEach는 외,내부 함수를 비동기로 처리해보고 검색도 해봤지만 실패...)

 

원래 목적은 속도를 알아보는 것 아니었나...?

for와 forEach(), 그리고 map()등의 반복문 속도를 검색해보았으나 결과들이 모두 달랐다.

알고보니 브라우저의 엔진에 따라 결과가 나뉜다고 함! (그래도 대체적으로 for문이 빠름)

(출처:https://velog.io/@zuyonze/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%98%EB%AC%B8-glk00t4bxk)

 

다음 포스트(https://mason-lee.tistory.com/3)에서는 forEach()와 map()의 차이와 Promise와 map()을 이용한 비동기식 병렬처리에 대해 알아보겟다.