1. JavaScript Promise Object
- Promise는 Producing code(생성코드)와 Consuming code(소비코드)를 연결하는 Javascript 객체입니다.
- 간단하게 말하자면 비동기 방식을 로딩, 성공, 실패로 나눠서 처리하는 객체입니다.
- Producing code(생성코드)는 시간이 걸릴 수 있는 코드입니다.
- Consuming code(소비코드) 는 결과를 기다려야 하는 코드입니다.
- Promise객체는 Producing code(생성코드) 와 Consuming code(소비코드)에 대한 호출이 모두 포함됩니다.
- 이 객체는 ES6에서 도입하기 시작했습니다.
## 문법
let 인스턴스변수명 = new Promise(function(testResolve, testReject){
//Producing Code 작성
testResolve(); //성공했을 때
testReject(); //에러가 발생했을 때
});
//Consuming Code 작성
인스턴스변수명.then(
function(value){ /* 성공했을때 코드 */ },
function(error){ /* 에러있을때 코드 */ }
);
- Producing code(생성코드)가 결과를 얻으려면 두 콜백(testResolve, testReject) 중 하나를 호출해야 합니다.
1) Promise 객체 속성
- Promise 객체는 크게 [로딩 중(Pending)], [성공(fulfilled)], [실패( rjected)] 이렇게 명령을 줄 수 있습니다.
- [ state ] 및 [ result ]라는 속성을 통해 위의 명령을 줄 수 있습니다.
- [로딩중]인 동안에는 결과가 정의되지 않습니다.
- [성공]되면 결과는 값입니다.
- [실패]되면 결과는 오류객체로 처리가 됩니다.
## 코드 보기
- 정수를 입력받아 양수면 잘 입력했다고 하고, 음수면 잘못입력했다고 처리해 보겠습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h2>JavaScript Promise</h2>
<p id="test"></p>
<script>
const test = document.getElementById('test');
function output(x){
test.innerHTML = x;
}
let testPromise = new Promise(function(testResolve, testReject){
let num = prompt('양수를 입력하세요','정수입력');
if(num > 0){
testResolve('잘 입력하셨습니다.');
}else{
testReject('잘못 입력하셨습니다.');
}
});
testPromise.then(
function(value){ output(value); },
function(error){ output(error); }
);
</script>
</body>
</html>
## 결과 보기
- 결과가 잘 도출되는 것을 확인할 수 있습니다.
2) setTimeout을 기다리도록 처리
## 콜백함수를 이용했던 코드
- 지난 강좌에서 비동기로 다른 함수와 setTimeout과의 처리를 해봤습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h3>일반함수 값: <span id="test01"></span></h3>
<h3>setTimeout 값: <span id="test02"></span></h3>
<script>
//문서객체 선택
const test01 = document.getElementById('test01');
const test02 = document.getElementById('test02');
function output01(){
test01.innerHTML = '저는 일반함수 값입니다!';
}
function output02(){
test02.innerHTML = '저는 setTimeout에서 실행되었습니다.';
}
setTimeout(output02,3000);
output01();
</script>
</body>
</html>
- [ output02() ]를 먼저 [ setTimeout() ]에 호출하고, [ output01() ]를 나중에 호출하였습니다.
- 하지만 [ output02() ]는 3초 후에 실행되고, [ output01() ]이 먼저 실행됩니다.
- 이것을 promise로 처리가 가능합니다.
## Promise를 이용한 코드 보기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h3>일반함수 값: <span id="test01"></span></h3>
<h3>setTimeout 값: <span id="test02"></span></h3>
<script>
//문서객체 선택
const test01 = document.getElementById('test01');
const test02 = document.getElementById('test02');
function output01(){
test01.innerHTML = '저는 일반함수 값입니다!';
}
const output02 = new Promise(function(testResolve, testReject){
setTimeout(function(){ testResolve('저는 Promise에서 setTimeout으로 실행되었습니다.'); },3000);
});
output02.then(function(value){
test02.innerHTML = value;
});
output01();
</script>
</body>
</html>
- 역시 [ output02() ]를 promise로 호출하였고, [ output01() ]를 나중에 호출하였습니다.
- 하지만 [ output02() ]는 3초 후에 실행되고, [ output01() ]이 먼저 실행됩니다.
## 결과보기
- promise로도 잘 처리되는 것을 확인할 수 있습니다.
3) file 불러오는 것을 처리
## 불러올 [soccer.html]
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>테스트 페이지</title>
</head>
<body>
<h3>2022년 카타르 월드컵</h3>
<img src="https://cdn.pixabay.com/photo/2016/05/20/21/57/football-1406106_960_720.jpg" width="400" alt="">
<p>카타르 월드컵이 시작되었습니다.</p>
</body>
</html>
- 위와 같은 파일을 JS를 통해 불러온다고 보도록 하겠습니다.
## callback을 이용한 코드 보기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h3>soccer.html 불러오기</h3>
<p id="test"></p>
<script>
//문서객체 선택
const test = document.getElementById('test');
function output(html){
test.innerHTML = html;
}
function getFile(testCallback){
let req = new XMLHttpRequest();
req.onload = function(){
if(req.status = 200){
testCallback(this.responseText);
}else{
testCallback("Error : " + req.status);
}
}
req.open('GET','soccer.html');
req.send();
}
getFile(output);
</script>
</body>
</html>
## Promise를 이용한 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h3>soccer.html 불러오기</h3>
<p id="test"></p>
<script>
//문서객체 선택
const test = document.getElementById('test');
function output(html){
test.innerHTML = html;
}
let testPromise = new Promise(function(testResolve, testReject){
let req = new XMLHttpRequest();
req.open('GET','soccer.html');
req.onload = function(){
if(req.status == 200){
testResolve(req.response);
}else{
testReject('File not Found');
}
}
req.send();
});
testPromise.then(
function(value){ output(value); },
function(error){ output(error); }
);
</script>
</body>
</html>
## 결과 보기
- 서버환경에서만 처리되므로 VS CODE [Go Live]를 눌러주세요.
4) 서버 데이터 불러오기
- 서버에서의 데이터 통신이므로 jsonplaceholder사이트를 이용해 보겠습니다.
- jsonplaceholder사이트는 json데이터를 가져다 쓸 수 있게 해주는 무료 가짜 REST API입니다.
- 원래 타 사이트의 데이터는 크로스도메인 관련 문제 때문에 함부로 가져다 쓸 수는 없습니다.
https://jsonplaceholder.typicode.com/
## Promise를 통한 서버데이터 불러오기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS - Promise</title>
</head>
<body>
<h3>jsonplaceholder 데이터 불러오기</h3>
<p id="test"></p>
<script>
//문서객체 선택
const test = document.getElementById('test');
function output(html){
test.innerHTML = html;
}
let testPromise = new Promise(function(testResolve, testReject){
output('로딩중...');
let req = new XMLHttpRequest();
req.open('GET','https://jsonplaceholder.typicode.com/posts/1');
req.setRequestHeader("content-type", "application.json")
req.onload = function(){
if(req.status == 200){
testResolve(req.response);
}else{
testReject('File not Found');
}
}
req.send();
});
testPromise.then(
function(value){ output(value); },
function(error){ output(error); }
);
</script>
</body>
</html>
## 데이터 가져오는 것이 성공했을 때
- 데이터를 가져오기 전까지는 로딩중이라고 뜨는 것이 확인됩니다.
- 데이터 가져오는 것이 성공하면 위와 같은 화면이 나옵니다.
## 데이터 가져오는 것이 실패했을 때
- 위는 주소를 일부러 틀리게 해봤습니다. 그럼 파일을 찾을 수 없다고 나옵니다.
2. 콜백지옥(Callback Hell)
- 예전에는 비동기적인 작업을 할 때 콜백함수를 통해서 처리를 했습니다.
- 하지만 콜백함수로 처리를 하게 된다면 비동기 작업이 많아질 경우 코드가 쉽게 난잡해집니다.
- 그것을 콜백지옥이라고 부릅니다.
1) 콜백함수로 비동기식 작업 처리
- num을 파라미터 받아와서 1초마다 1씩 더해서 출력하는 작업을 setTimeout으로 해보겠습니다.
## 콜백 지옥 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Promise</title>
</head>
<body>
<script>
function numIncrease(num, callback){
setTimeout(() => {
const numPlus = num + 1;
console.log(numPlus);
if(callback){
callback(numPlus);
}
},1000);
}
numIncrease(0, num => {
numIncrease(num, num => {
numIncrease(num, num => {
numIncrease(num, num => {
numIncrease(num, num => {
console.log('다섯번다했다!');
});
});
});
});
});
</script>
</body>
</html>
- 엄청난 콜백 지옥이죠?
## 결과 보기
- 결과는 잘 나오는 것이 확인됩니다.
- 비동기적으로 처리해야 하는 일이 많아질수록, 코드의 깊이가 계속 깊어지는 현상이 발생됩니다.
- 이럴 때 Promise를 사용하면 코드의 깊이가 깊어지는 현상을 방지할 수 있습니다.
2) Promise로 비동기식 작업 처리
- num을 파라미터 받아와서 1초마다 1씩 더해서 출력하는 작업을 Promise으로 해보겠습니다.
## 콜백지옥 벗어나는 코드
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>Promise</title>
</head>
<body>
<script>
function numIncrease(num){
return new Promise((testResolve, testReject) => {
setTimeout(() => {
const numPlus = num + 1;
if(numPlus > 5){
const error = new Error();
error.name = '다섯번다했다!';
testReject(error);
return;
}
console.log(numPlus);
testResolve(numPlus);
},1000);
});
}
numIncrease(0)
.then(numIncrease)
.then(numIncrease)
.then(numIncrease)
.then(numIncrease)
.then(numIncrease)
.catch(e => {
console.error(e);
});
</script>
</body>
</html>
- 일단 Promise를 사용하면 비동기 작업이 많아져도 코드의 깊이는 깊어지진 않습니다.
- 하지만 이 코드가 편한 가는 의구심이 들죠.
- 지금은 간단한 구문이어서 그렇지, 실제로는 에러가 발생되었을 때 몇번째 줄에서 에러가 났는지 확인하기 어렵습니다.
- 그래서 async/await 까지 사용하면, 이런 문제점을 해결할 수 있습니다.
- 다음 강좌에서 async/await 를 배워보도록 하겠습니다.
## 결과 보기
- 결과는 역시 잘 나오는 것이 확인됩니다.
'웹언어 > 자바스크립트 - ES6' 카테고리의 다른 글
[ES6강좌] 12강 Tagged Template literals - 오쌤의 니가스터디 (0) | 2023.03.02 |
---|---|
[ES6강좌] 11강 Template literals - 오쌤의 니가스터디 (0) | 2023.03.02 |
[ES6강좌] 9강 JS Asynchronous(비동기) - 오쌤의 니가스터디 (0) | 2022.11.23 |
[ES6강좌] 8강 JS Callback - 오쌤의 니가스터디 (0) | 2022.11.23 |
[ES6강좌] 8강 반복문 - for of - 오쌤의 니가스터디 (0) | 2022.11.08 |