Promise란
자바스크립트 비동기 처리에 사용되는 객체
비동기 작업의 미래의 진행, 성공, 실패 상태와 처리의 순서를 표현한다.
<3가지 states>
- 대기 pending: 이행하지도, 거부하지도 않은 초기 상태
- 이행 fulfilled: 연산이 성공적으로 완료
- 거부 rejected: 연산 실패
Promise가 필요한 이유
프로미스는 주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.
$.get('url 주소/products/1', function(response) {
// ...
});
만약 데이터를 요청하고 받아오기 위해 위의 API를 실행하면 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 마냥 화면에 데이터를 표시하려 한다. 그러면 오류가 발생하거나 빈 화면이 뜬다.
이것을 해결하기 위한 한 가지 방법이 프로미스이다.
❓동기면 한 라인씩 완료시키며 작업하는 것인데 왜 굳이 비동기에서 await를 써가며 작업을 기다리는가?
=> 동기 코드는 blocking으로 인해 한 라인씩 진행하면서 완료될 때 까지 코드 실행이 차단되기 때문이다.
비동기 코드는 일시 중단 되어 비동기 작업을 기다린다고 할 수 있다. 비동기는 await를 사용하여 동기 코드와 유사한 방식으로 동작시킨다고 볼 수 있다.
❓그렇다면 왜 비동기 코드를 동기코드처럼 보이도록 하는가?
동기 코드로 쓴다면 자바스크립트는 기본적으로 싱글 스레드에서 동작하므로 blocking 작업을 수행하면 전체 어플리케이션이 멈추게 된다. 이러한 동기식 접근은 CPU 집약적인 작업에서 사용되며, 비동기 작업에는 적합하지 않다.
await를 사용하여 비동기 코드를 동기 코드처럼 작성한다. 비동기 작업을 수행하면서도 다른 작업을 동시에 처리할 수 있게 하며 코드를 블로킹하지 않는다. (효율적으로 동작 & 빠르게 응답)
블로킹 blocking
- 작업을 실행시키는 방식 중 하나
- 하나의 작업을 실행시킨 후 해당 작업이 끝날 때 까지 기다린다. 그 후에 다음 작업을 실행시킨다.
- 자바스크립트에서는 동기와 혼용된다.
논블로킹 non-blocking
- 하나의 작업을 실행시키고 그 작업이 마치지 않아도 다음 작업을 실행하는 실행방식
- 작업을 실행시켜 놓기만 하는 행위
- 실행시킨 작업이 길어져도 다음 작업이 지연이 되는 문제가 없다
- 자바스크립트에서는 비동기와 혼용된다.
- 기술적으로 논블로킹 방식은 싱글 스레드로만은 불가
Promise Method
let promise = new
Promise((resolve, reject) =>
{
if (Math.random() < 0.5) {
return reject("실패")
}
resolve(10)
})
promise
.then(data => {
console.log("성공:", data);
})
.catch(error => {
console.error("실패:", error);
})
.finally(() => {
console.log("promise 종료")
})
- new Promise (callback): promise 객체 생성
- callback 함수는 (resolve, reject) 두 인자를 받는다.
- Promise가 성공했을 때 resolve를, 실패했을 때는 reject를 호출한다.
- then() 메서드에 성공했을 때 실행할 콜백 함수를 인자로 넘긴다.
- 첫 함수가 성공적으로 실행되면 첫 .then()을 시작으로 성공적으로 실행된다면 순차적으로 결과를 전달하고 다음 작업을 실행된다.
- catch() 메서드에 실패 했을 때 실행할 콜백 함수를 인자로 넘긴다.
- finally() 메서드는 성공/실패 여부와 상관없이 모두 실행할 콜백 함수를 인자로 넘긴다.
- then(callback1, callback2)로 callback1에 성공, callback2에 실패 메서드를 인자로 넘길 수 있다.
promise.then(
result => {
console.log("성공:", result);
},
error => {
console.error("실패:", error);
}
);
- Promise.all()은 promise의 배열을 받아 모두 성공 시 각 promise의 resolved 값을 배열로 반환한다. 하나의 promise라도 실패할 시, 가장 먼저 실패한 promise의 실패 이유를 반환한다.
Promise.all([
promise1,
promise2,
promise3
])
.then(values => {
console.log("모두 성공: ", values)
})
.catch(e => {
console.log("하나라도 실패: ", e)
})
<여러 반환 방법>
1. 직접 반환
function fetchData() {
return new Promise((resolve, reject) => {
// 가정: 비동기 작업 (예: 네트워크 요청)
const isSuccessful = true; // 비동기 작업이 성공했다고 가정
if (isSuccessful) {
const data = "데이터를 가져왔습니다.";
resolve(data); // 성공적으로 작업을 완료한 경우
} else {
reject(new Error("데이터를 가져오는 데 실패했습니다.")); // 실패한 경우
}
});
}
// 사용법
fetchData()
.then(data => {
console.log(data); // 성공적으로 데이터를 가져온 경우 출력
})
.catch(error => {
console.error(error.message); // 작업 실패 시 에러 출력
});
2. 변수 생성 후 반환
const myPromise = new Promise((res, rej) => {
~~~
})
return myPromise
Promise Method Chain
: 객체에 메서드를 연결할 수 있는 것. 연속적인 작업을 할 수 있다.
promise
.then(data => {
return fetchUser(data);
})
.then(user => {
console.log('User : ', user)
})
.catch(e => {
console.log('실패 : ', e)
})
- then/catch 메서드가 또 다른 promise를 리턴하여 비동기 코드에 순서를 부여한다.
- 함수를 호출한 주체가 함수를 끝낸 뒤 자기 자신을 리턴하도록 하여 구현한다.
예시
1. 네트워크 요청 처리 (Ajax)
function fetchData(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(response => {
if (!response.ok) {
reject(new Error("네트워크 오류"));
}
return response.json();
})
.then(data => {
resolve(data);
})
.catch(error => {
reject(error);
});
});
}
// 사용 예제
fetchData("https://api.example.com/data")
.then(data => {
console.log("데이터 가져오기 성공:", data);
})
.catch(error => {
console.error("오류 발생:", error);
});
2. 파일 읽기와 쓰기
const fs = require('fs');
function readFileAsync(filePath) {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
// 사용 예제
readFileAsync('example.txt')
.then(data => {
console.log("파일 읽기 성공:", data);
})
.catch(error => {
console.error("파일 읽기 오류:", error);
});
3. 병렬 작업 처리 (Promise.all)
const promise1 = fetch("https://api.example.com/data1");
const promise2 = fetch("https://api.example.com/data2");
const promise3 = fetch("https://api.example.com/data3");
Promise.all([promise1, promise2, promise3])
.then(responses => {
const results = responses.map(response => response.json());
return Promise.all(results);
})
.then(dataArray => {
console.log("모든 데이터:", dataArray);
})
.catch(error => {
console.error("오류 발생:", error);
});
async & await
.then() 메서드를 연속적으로 체이닝 하는 대신 await를 사용하면 코드의 가독성이 향상된다.
<기본적인 .then() 사용>
fetchData()
.then((data) => {
console.log('데이터를 가져왔습니다: ', data);
return process(data);
})
.then((result) => {
console.log('처리 결과:', result);
})
.catch((error) => {
console.error('에러 발생', error);
});
<async & await 사용>
async function fetchDataAndProcess() {
try {
const data = await fetchData();
console.log('데이터를 가져왔습니다:', data);
const result = await process(data);
console.log('처리 결과:', result);
} catch (error) {
console.error('에러 발생', error);
}
}
fetchDataAndProcess();
<Step>
1. fetcjDataAndProcess() 함수 호출
2. 함수 내부에서 try 블록 시작
3. fetchData() 함수가 호출되고, 해당 함수는 비동기적으로 데이터를 가져오는 작업을 시작. 이때 프로그램은 await fetchData()에서 멈추고 데이터가 완전히 가져올 때 까지 기다림
4. fetchData() 함수가 promise를 반환. 이 promise가 이행되면 const data = await fetchData(); 라인에서 data 변수에 결과 값을 할당하고 데이터가 성공적으로 가져와지면 console.log문 실행
5. process(data) 함수가 호출되고 비동기 작업 시작. await process(data)에서 멈추고 데이터 처리가 끝날 때 까지 기다림.
...
'Front-end > JavaScript' 카테고리의 다른 글
| [JavaScript] reduce() 이해하기 (0) | 2023.09.28 |
|---|---|
| [JavaScript] Execution Context 실행 컨텍스트 (0) | 2023.09.24 |
| [JavaScript] classList 사용법 (0) | 2023.07.15 |
| [JavaScript] innerHTML, innerText, textContent 차이점 (0) | 2023.07.02 |
| 호이스팅과 Temporal Dead Zone (0) | 2023.06.28 |