JS 개념정리

[Javascript 알짜개념] 비동기 fetch, async/await, then, 반복문

furaha 2023. 8. 16. 14:26
반응형

일단 먼저 정리해두고 싶은 부분은

용어를 제대로 알지 못하고 섞어서 이것 저것 참고하다보니까

용어들이 많이 헷갈렸었다

 

Promise 함수를 사용할 때,

then 혹은 async/await 둘 중 하나를 사용하면 된다.

 

then의 형식은 체이닝 형식이고

 

a()

.then(b())

.catch(c())

.finally(console.log('Done!'))

async/await 형식은 블록 형식이다

 

const wrap = async () => {
  try {
   const res = await a()

   console.log(res)

  } 
  catch (err) {
   console.error(err)
  }
  finally {
   console.log('Done!')
  }
}

 

async/await 는 블록 안에 함수들을 넣기 때문에,

Promise() 매개변수 안에 resolve, reject 를 사용할 필요가 없다

 

반대로 then 문법을 사용하려면

resolve, reject 를 Promise() 매개변수 안에 사용한다.

then() 의 내용이 resolve 이고, catch() 의 내용이 reject 이다.

 


동기 / 비동기의 말의 뜻을 알아보자

 

동기 : 순차적으로 코드 실행됨

비동기 : 순차적으로 코드 실행하지 않음 (코드 작성은 순차적으로 했으나, 코드 실행은 순차적이지 않음)

 

제일 대표적인 예시가 setTimeout() 이다

제일 마지막에 실행되고 있다. 자세한 이유에 대해서는 이 포스팅에 나와있다

 

console.log(1)
setTimeout((console.log(2) => {}, 1000)
console.log(3)

// 1
// 3
// 2

 

const btnEl = document.querySelector('h1')
btnEl.addEventListener('click', () => {
	console.log('first')
})

console.log('second')


// second
// '클릭했을 때' first

 

사실 우리는 비동기 방식을 계속 사용하고 있었다

이벤트 거는 것을 제일 많이 하기 때문에,,

 

데이터를 불러오는데에는 비동기 코드를 필수적으로 사용해야한다

 

아래 코드를 보자

 

fetch('정보가 담긴 url')
  .then(res => res.json())
  .then(res => {
    console.log(res)
})
    
console.log(1)
console.log(2)
console.log(3)

 

fetch 라는 것은 해당 url 의 서버로 데이터를 요청하고 응답을 받아오는 메소드이다

그런데 데이터 응답이 오기까지는 특정 시간이 필요하다

그래서 동기적으로 진행하면 나머지 코드들이 기다려야하기 때문에

그래서 비동기는 서버로 데이터를 요청해놓고 응답이 왔던지 오지 안았던지 일단 다음 코드를 진행하게 한다

 

 

그런데 내가 데이터를 불러온 후에야 함수들을 실행하도록 특정 순서를 지정하고 싶다면

그 함수들을 then 안의 콜백함수안에 넣으면 된다

 

fetch('정보가 담긴 url')
	.then(res => res.json())
	.then(res => {
    	console.log(res)
        console.log(1)
        console.log(2)
        console.log(3)
})

 


 

아래 코드는 각각 3개의 데이터를 요청하는 함수를 불러오고 있다

그런데 순서는 보장하지 못한다. 왜냐하면 데이터가 먼저 준비되는대로 불러와지니까

 

const getMovies = (movieName, callback) => {
	fetch('https://www.omdbapi.com/?apikey=7035c60c&s=${movieName}')
    .then(res => res.json())
    .then(res => {
    	console.log(res)
        callback()
    })
}

getMovies('frozen', () => {
	console.log('겨울왕국');
})

getMovies('frozen', () => {
	console.log('어벤져스');
})

getMovies('frozen', () => {
	console.log('아바타');
})

//여기서 순서는 데이터를 응답받는 순서에 따라 다르기 때문에
//저 순서대로 나오지 않을 수 있음

 

그래서 순서를 보장하려면 아래 코드처럼

콜백 함수로 불러올 수 있다. 

 

const getMovies = (movieName, callback) => {
	fetch('https://www.omdbapi.com/?apikey=7035c60c&s=${movieName}')
	.then(res => res.json())
	.then(res => {
	  console.log(res)
	  callback()
	})
}

getMovies('frozen', () => {
	console.log('겨울왕국');
	getMovies('frozen', () => {
	  console.log('어벤져스');
	    getMovies('frozen', () => {
	      console.log('아바타');
	    })
	})
})



// 이렇게 하면 순서가 보장될 수 있음

 

그러나 주의사항은

이런 사태가 발생할 수 있다,,

 

그래서 무엇이 나왔냐? Promise 클래스

 

맨 위에 적어두었던

then async/await 문법들은 다 Promise 클래스의 메소드들이고

이들은 콜백지옥을 피하기 위해 나온 메소드들이다

 

Promise 와 then 을 같이 사용하는 코드를 보자

 

const a = () => {
	return new Promise(resolve => {
    	setTimeout(() => {
        	console.log(1)
            resolve()
        }, 1000)
    })
}

const b = () => {
	return new Promise(resolve => {
    	setTimeout(() => {
        	console.log(2)
            resolve()
        }, 1000)
    })
}

const c = () => {
	return new Promise(resolve => {
    	setTimeout(() => {
        	console.log(3)
            resolve()
        }, 1000)
    })
}

const d = () => console.log(4)

a()
  .then(b)
  .then(c)
  .then(d)
    
// 이렇게 체이닝 형태로 나열하면서 정리해줄 수 있음

 

async await 를 써보자

 

const a = () => {
	return new Promise(resolve => {
    	setTimeout(() => {
        	console.log(1)
            resolve()
        }, 1000)
    })
}

const b = () => console.log(2)
a().then(() => b())

// 이것을 async await 로 바꿔보면

const wrap = async () => {
	await a()
	b()
}

wrap()

 


 

Resolve, Reject 그리고 에러 헨들링

 

const delayAdd = (index, cb, errorCb) => {
	setTimeout(() => {
    	if (index > 10){
        	errorCb(`${index}는 10보다 클 수 없습니다`)
            return
        }
        console.log(index)
        cb(index + 1)
    }, 1000)
}

delayAdd(
	4,
    res => console.log(res),
    err => console.error(err)
)

 

위의 일반 코드를 Promise 를 사용한 코드로 바꿔보자

 

const delayAdd = index => {
	return new Promise((resolve, reject) => {
    	setTimeout(() => {
        	if(index > 10){
            	reject(`${index}는 10보다 클 수 없습니다`)
                return
            }
            console.log(index)
            resolve(index + 1)
        }, 1000)
    })
}

delayAdd(13)
  .then(res => console.log(res))
  .catch(err => console.error(err))

 

이게 또 async await 패턴에서는

 

const wrap = async () => {
  try {
   const res = await delayAdd(2)
   console.log(res)
  } 
  catch (err) {
   console.error(err)
  }
  finally {
   console.log('Done!')
  }
}

어떻게 보면 async/await 가 더 깔끔해보이기도 하다

 

 


 

반복문에서의 비동기 함수 호출 팁!!

 

forEach는 하나씩 배회하고, 데이터가 불러와지는 순서대로 찍히기 때문에 순서를 보장할 수 없다

그러나 순서를 보장해야 한다면! for문을 쓰면된다!!

 

const getMovies = movieName => {
	return new Promise(resolve => {
    	fetch(`https://www.omdbapi.com/?apikey=7035c60c&s=${movieName}`)
	.then(res => res.json())
	.then(res => resolve(res))
    })
}

const titles = ['frozen', 'avengers', 'avatar']

// forEach로는 순서대로 불러지지 않고 계속 바뀔 수 있음
titles.forEach(async title => {
	const movies = await getMovies(title)
	console.log(title, movies)
})

// for문은 하나 하나 데이터가 불러와지고 블록을 탈출하기 때문에 순서대로 불러와짐
const wrap  = async () => {
	for (const title of titles){
    	const movies = await getMovies(title)
        console.log(title, movies)
    }
}

wrap()

 

 


 

마지막으로 fetch

 

fetch() 함수는 Promise 객체를 반환하는 비동기 함수이다

쓰여지는 형태는 fetch(주소, 옵션) 이렇게 쓰인다

 

fetch 를 옵션과 함께 사용해보자

 

// fetch 사용해보기

fetch('https://www.omdbapi.com/?apikey=7035c60c&s=avengers', {
  method : 'GET' // GET 은 기본옵션임. POST, PUT, DELETE 
  headers : {
    'Content-Type' : 'application/json'  // json 포맷으로 전송할 수 있다는 뜻
  },
  body : JSON.stringify({
    name : 'HEROPY',
    age : 85,
    email : '123@gmail.com'
  })   // 문자화를 시켜야 해서 JSON.stringify 메소드 사용
})
  .then(res => res.json())
  .then(json => console.log(json))
    
const wrap = async () => {
  const res = await fetch('https://www.omdbapi.com/?apikey=7035c60c&s=avengers')
  const json = await res.json()
  console.log(json)
}

wrap()

 

fetch 옵션으로는 더 많은 옵션들이 있다

method, headers, body, mode, cache, credentials, redirect, referrer, referrerPolicy 등등

다음에 차근차근 하나씩 써보자

반응형