비동기 요청이 여러 개 있는 상황에서, 하나의 요청이 다른 요청 결과에 의존할 때
const response1 = request(‘https://example.com’)
const response2 = request(‘https://example.com’, response1)
만약 위처럼 둘 다 비동기 요청을 보내는데, 두 번째 요청을 보낼 때 첫 번째 요청의 결과가 필요할 수 있다. 이런 경우 둘 다 병렬적으로 요청을 보내기 때문에, response1을 가지기 전에 두 번째 요청이 보내진다.
이런 부분을 해결하기 위해 콜백, Promise, Async/Await를 사용할 수 있다.
콜백함수
콜백 함수는 특정 함수에 매개변수로 전달된 함수를 의미한다. 그리고 그 콜백함수는 함수를 전달받은 함수 안에서 호출된다.
function firstFunction(parameters, callback){
const response1 = request('...');
callback(response1);
}
function secondFunction(response1, callback){
const response2 = request('...');
callback();
}
firstFunction(para, function(response1){
secondFunction(response1, function(){
thirdFunction(para,function(){
// ...
})
})
})
하지만 가독성이 떨어지고, 에러 처리를 한다면 모든 콜백에서 각각 에러 핸들링을 해주어야 한다.
Promise
Promise 객체는 new 키워드와 생성자를 이용해서 만든다. 생성자는 매개변수로 실행 함수를 받는다. 실행 함수의 매개변수는 두 가지로, 첫 번째(resolve)는 비동기 작업을 성공적으로 완료해 결과를 값으로 반환할 때호출해야 하고, 두 번째는 작업이 실패하여 오류를 반환할 때 호출하면 된다.
function fetchData(){
return new Promise((resolve, reject) => {
const success = true;
if(success){
resolve('성공!');
} else {
reject('실패 ㅠㅠ');
}
})
}
fetchData()
.then((result)=>{
console.log(result);
}).catch((error)=>{
console.log(error);
})
비동기 작업이 성공했을 때 resolve를 반환, 실패했을 때 reject를 반환하므로, 위의 코드에서는 resolve를 반환해서 ‘성공!’이 출력될 것이다.
Promise의 상태
대기(pending): 비공기 처리 로직이 아직 완료되지 않은 상태.
이행(fulfilled): 비동기 처리가 완료되어 프로미스가 결과 값을 반환해 준 상태.
거부(rejected): 비동기 처리가 실패하거나 오류가 발생한 상태.
비동기 요청이 대기 중일 때의 상태는 pending이고, 결괏값은 undefined이다. 성공했을 때는 fulfilled와 결과 값으로 value를, 실패했을 때는 rejected와 error의 결괏값을 가진다.
myPromise
.then((result) => {
console.log(result);
}).catch((error) => {
console.log(error);
}).finally(() => {
console.log('모든 작업 끝!');
})
then으로 resolve 값이, catch로 reject값이 간다. 그리고 이행이든 거부든, 마지막에 finally이 실행된다.
예제
fetch 함수는 첫 번째 인자로 URL, 두 번째 인자로 옵션 객체를 받고, Promise 타입의 객체를 반환한다. API 호출이 성공했 응답 객체를 resolve 하고, 실패했을 경우 예외 객체를 reject 한다.
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response1 => response1.json())
.then(json1 => console.log(json1))
.then(() => fetch('https://jsonplaceholder.typicode.com/todos/2'))
.then(response2 => response2.json())
.then(json2 => console.log(json2))
.catch(error => console.error(error))
.finally(() => console.log('모든 작업 끝!'));
실제로 비동기 요청을 보낸 결과이다. 성공한 값이 잘 나타나는 것을 확인할 수 있다.
두 번째 요청의 경로를 이상하게 바꿔서 보낸 결과이다. 코드가 reject 되어를 통해 출력되고 있는 것을 확인할 수 있다.
Async/Await
함수의 앞에 async를 붙이면 ‘이 함수는 비동기 함수이고 Promise를 반환한다’라고 선언하는 것이다. 반환 값이 Promise 생성 함수가 아니어도 반환되는 값을 Promise 객체에 넣는 것이다.
Promise를 반환하는 함수 앞에 await를 붙이면 해당 Promise의 상태가 바뀔 때까지 코드가 기다린다. Promise가 성공 상태 또는 실패 상태로 바뀌기 전까지는 다음 연산을 시작하지 않는 것이다.
async function makeRequest() {
try {
const response1 = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const data1 = await response1.json();
console.log(data1);
const response2 = await fetch('https://jsonplaceholder.typicode.com/todos/2');
const data2 = await response2.json();
console.log(data2);
} catch (error) {
console.log(error);
} finally {
console.log("작업 끝~~")
}
}
makeRequest();
위의 then() 메서드와 같은 결과를 내는 코드이다. 비동기 코드인데 마치 동기 코드처럼 보이고, then 메서드를 사용하는 것보다 가독성이 좋다.
동기식 코드에서 쓰는 try/catch 구문을 async/await구조에서 사용할 수 있다.
왼쪽이 then 메서드를 활용한 코드, 오른쪽이 async/await를 사용한 코드이다. 확실히 오른쪽의 가독성이 더 좋은 것을 확인할 수 있다.
'프로그래밍 언어 > JavaScript' 카테고리의 다른 글
[JS] ES6 클래스 문법 (0) | 2024.04.29 |
---|---|
[JS] Prototype (0) | 2024.04.29 |
[JS] 순수함수(Pure Function) (0) | 2024.04.29 |
[JS] Intersection observer (0) | 2024.04.27 |
[JS] 함수 선언식과 표현식, 즉시 실행 함수(IIFE) (0) | 2024.04.26 |