33일차(4)/javascript : Promise
- javascript Promise 객체 사용법, 예제
- javascript는 비동기 방식으로 처리되고, 스레드가 하나뿐이다.
그래서 순서대로 어떤 작업을 하려고 할 때 좀더 복잡한 절차가 필요할 수 있다.
<test01.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test01.jsp</title>
</head>
<body>
<h1>비동기 테스트</h1>
<script>
setTimeout(function(){
console.log("5초가 경과됐네?");
}, 5000);
//가상의 주어진 작업을 처리 하는 함수 (주어진 작업을 처리하는데 random 한 시간이 걸린다고 가정하자)
function work(job, callback){
//0 ~ 5000 사이의 랜덤한 숫자 얻어내기
let ranTime=Math.random()*5000;
// 첫번째 인자로 전달된 함수를 random 한 이후에 호출하기
setTimeout(function(){
console.log(job+" 을(를) 수행 했습니다.");
//callback 안에는 function type이 들어있다는 가정하에서 해당 함수를 호출한다.
callback();
}, ranTime);
}
work("냉장고 문을 연다", function(){
work("코끼리를 넣는다", function(){
work("냉장고 문을 닫는다", function(){
});
});
});
/*
work("달리기", function(){
work("쉬기", function(){
work("잠자기", function(){
});
});
});
*/
//1. Promise 객체를 생성해서 참조값을 p1 에 담기
let p1=new Promise(function(resolve){
/*
함수의 인자로 전달되는 resolve는 함수이다.
resolve 는 작업을 완료 했을때 호출 해야하는 함수
resolve 함수가 호출되면 .then() 안에 있는 함수가 자동 호출된다.
*/
resolve();
});
p1.then(function(){
console.log("then 안에 있는 함수가 호출되었습니다.");
});
</script>
</body>
</html>
setTimeout(function(){
console.log("5초가 경과됐네?");
}, 5000);
- 어떤 작업이 5초 후 처리되도록 했을 경우,
- 5초 뒤에 호출되는 함수이다.
function work(job, callback){}
- work는 문자열, 함수를 전달하는 함수이다.
- 문자열로 전달한 작업이 끝나면 이 함수를 호출하는 구조
- 이 함수를 호출하게 되면 임의의 시간(ranTime) 이후에 이 작업이 끝난다.
- work 함수를 호출하면서 함수를 하나 전달하는 것이다.
work("냉장고 문을 연다");
work("코끼리를 넣는다");
work("냉장고 문을 닫는다");
- 위 작업은 순서대로 일어나야 한다.
- 하지만 실행해보면 순서가 일정하지않다. 작업이 수행되는 데 걸리는 시간이 랜덤이기 때문에 무엇이 먼저 끝날지 알 수 없다.
- javascript는 비동기 방식으로 처리된다.
ex) 두 가지 물건을 사용한다고 하자.
1) 커피컵 : 주문하면 일정 시간 후에 받을 수 있다.
2) 커피머신 : 버튼을 누르면 나온다.
→ 커피컵을 먼저 주문해도, 반드시 커피컵이 먼저 나오리라는 보장은 없다.
- 이 문제의 해결방법은?
1. 커피컵을 주문하고, 컵이 나올때까지 기다린 다음 주문한 컵이 나오면
커피머신의 버튼을 눌러서 컵을 받치고 커피가 나오길 기다린다.
→ 문제가 발생하지는 않지만, 컵을 기다리는 동안은 다른 일을 하지 못한다. javascript는 이렇게 일하면 안된다.
- javascript는 일하는 사람이 한명이다! 커피컵이 나올때까지 그 시간동안 다른 작업을 하지 못한다.
- javascript는 single thread 구조이다.
- java의 경우, 새 thread를 사용해서 동시에 여러 일을 진행할 수 있다. 그 사이에 메인 thread는 다른 일을 처리할 수 있다.
→ 이렇게 처리하는 것을 동기 처리 라고 부른다.
2. 커피컵을 주문하고, 컵이 나오면 저를 불러주세요~ 한다. (그동안 저는 다른 일을 할게 있어요)
컵이 나왔다고 부르면 컵을 받아서 커피머신에 컵을 받치고 버튼을 누른 후,
다른 데로 간다(커피가 다 나오면 저를 불러주세요~)
→ 이렇게 처리하는 것을 비동기 처리 라고 부른다.
- javascript는 비동기 처리: 함수를 하나 두고 갈 테니 함수를 호출해주세요~ 라고 생각하면 된다.
작업하고, 작업이 끝나면 이 함수 호출해줘~ 라는 함수가 callback함수.
- 비동기 처리의 주의사항 : 순서가 중요한 처리를 할때, javascript의 구조상 코드가 복잡해진다.
- javascript에서 순서가 중요한 처리를 할 때는 promise라는 객체를 사용해 어려움을 보완한다.
- javascript에서는 한 번에 여러 일을 처리하면 순서를 보장할 수가 없다.
- 다음 함수는 냉장고 문을 열고 나서 호출되도록 한다.
- work라는 함수를 호출하면서 전달한 함수는 저 일이 끝나면 알아서 호출된다.
→ 이런 함수를 callback 함수라고 한다.
- 이 함수 안에서 냉장고 문을 연 다음에 할 작업을 하면 된다.
- 저 function 안에 함수를 넣어주면 순서가 바뀔 염려는 없다!
- javascript는 이렇게 함수 안에서 함수가 처리되는 모양을 갖는다.
- 비동기 처리되는 작업을 순서대로 작업하고 싶으면 이렇게 하면 된다.
- 이것을 javascript 콜백 지옥이라고 부른다....
.then(function(){})
.then(function(){})
.then(function(){});
- 이를 개선하기 위해 promise가 나온 것!
- 왼쪽과 같은 작업을 오른쪽과 같은 모양으로 바꿀 수 있다. 1,2,3번 함수를 직렬화해서 나열한 것이다.
- promise는 javascript 의 기본 클래스 중 하나이다.(함수 형태) 클래스라고 생각해도 무방하다.
- new Promise() 하고 그 객체 안에 있는 것들을 어떻게 사용하는지를 익히면 된다.
- promise객체를 생성해서 .then함수를 호출하면서 함수(resolve)를 하나 전달하면
resolve가 콜되면서 알아서 then안에 들어있는 함수가 호출된다.
<test02>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test02.jsp</title>
</head>
<body>
<h1>Promise 테스트</h1>
<script>
//1. Promise 객체를 생성해서 참조값을 p1 에 담기
let p1=new Promise(function(resolve, reject){
/*
함수의 인자로 전달되는 resolve, reject는 함수이다.
resolve 는 작업을 완료 했을때 호출 해야하는 함수
reject 는 작업이 실패 했을때 호출 해야하는 함수
resolve 함수가 호출되면 .then() 안에 있는 함수가 자동 호출된다.
reject 함수가 호출되면 .catch() 안에 있는 함수가 자동 호출된다.
*/
resolve();
//reject();
console.log("reject 함수를 호출 했습니다.");
});
//2. p1 Promise 가 resolve 되었을때 호출되는 함수 등록
p1.then(function(){
console.log("then 안에 있는 함수가 호출되었습니다.");
});
//3. p1 Promise 가 reject 되었을때 호출되는 함수 등록
p1.catch(function(){
console.log("catch 안에 있는 함수가 호출되었습니다.")
});
</script>
</body>
</html>
- 함수의 인자로 전달되는 resolve, reject는 함수이다.
- resolve 는 작업을 완료했을때 호출해야하는 함수
reject 는 작업이 실패했을때 호출해야하는 함수
- resolve 함수가 호출되면 .then() 안에 있는 함수가 자동 호출된다.
reject 함수가 호출되면 .catch() 안에 있는 함수가 자동 호출된다.
- 작업이 정상적으로 완료되었을 때 resolve를 호출하면 .then이 실행됨
- 원하는 작업이 제대로 되지 않았을 때 reject가 호출되면서 .catch가 실행됨
let p1=new Promise(function(resolve, reject){ });
- 작업이 제대로 되면 resolve 호출, 실패하면 reject를 호출하도록 약속된 것!
(매개변수명은 자유롭게 지을 수 있다)
<test03>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test03.jsp</title>
</head>
<body>
<h1>Promise 테스트</h1>
<script>
//아래와 같은 형식으로 작성할수도 있다.
new Promise(function(resolve, reject){
resolve();
console.log("resolve 호출됨");
})
.then(function(){
console.log("then() 안에 있는 함수 호출됨");
})
.catch(function(){
console.log("catch() 안에 있는 함수 호출됨");
});
</script>
</body>
</html>
- 변수 p1에 담아놓고 쓸 수도 있지만, 좀더 편하게 쓰려면 .then / .catch 로 요약해서 쓸 수 있다.
- 객체를 생성하고나서 바로 . 찍어서 연속으로 쓸 수 있다.
<test04>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test04.jsp</title>
</head>
<body>
<h1>Promise 테스트</h1>
<script>
//아래와 같은 형식으로 작성할수도 있다.
new Promise(function(resolve, reject){
//비동기 작업의 결과 데이터가 있다면
let data={num:1, name:"바나나"};
//resolve() 함수를 호출하면서 전달할수도 있다.
resolve(data);
console.log("resolve 호출됨");
})
.then(function(result){
//resolve() 함수에 전달된 값이 then() 안에 있는 함수의 인자로 전달된다.
console.log(result);
console.log("then() 안에 있는 함수 호출됨");
})
.catch(function(){
console.log("catch() 안에 있는 함수 호출됨");
});
</script>
</body>
</html>
- resolve를 호출하면서 데이터를 전달하기
- resolve("어떤 값")을 .then(function("어떤 값"){ }); 안에서 인자로 받아서 사용한다.
<test05>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test05.jsp</title>
</head>
<body>
<script>
new Promise(function(resolve){
resolve();
})
.then(function(){
console.log("첫번째 then() 호출됨");
return new Promise(function(resolve){
resolve();
});
})
.then(function(){
console.log("두번째 then() 호출됨");
});
</script>
</body>
</html>
- 연두색: 첫번째 promise / 분홍색: 두번째 promise
- 리턴값 부분에 또 다른 promise를 생성해서 리턴하면 then을 연결할 수 있다.
- 작업에 성공하면 resolve를 호출. 실패하면 reject를 호출하기로 약속한 것!
<test06>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test06.jsp</title>
</head>
<body>
<script>
//가상의 주어진 작업을 처리 하는 함수 (주어진 작업을 처리하는데 random 한 시간이 걸린다고 가정하자)
function work(job, callback){
//0 ~ 5000 사이의 랜덤한 숫자 얻어내기
let ranTime=Math.random()*5000;
// 첫번째 인자로 전달된 함수를 random 한 이후에 호출하기
setTimeout(function(){
console.log(job+" 을(를) 수행 했습니다.");
callback();
}, ranTime);
}
new Promise(function(resolve){
//첫번째 작업을 한다.
work("달리기", resolve);
})
.then(function(){
return new Promise(function(resolve){
work("쉬기", resolve);
});
})
.then(function(){
return new Promise(function(resolve){
work("잠자기", resolve);
});
})
.then(function(){
console.log("모든 작업을 순서대로 잘 처리 했습니다.");
});
</script>
</body>
</html>
- 어떤 작업을 순서대로 처리할 때 .then을 사용해서 순차적으로 처리되도록 한다.
- 1번은 에러처리가 났을 때 처리하기가 힘들다..
- 그래도 2번은 catch가 있어서 예외사항이 발생했을 때 처리가 수월하다.
- 사실 new Promise 할 일은 별로 없다.
보통 javascript의 라이브러리가 리턴해주는 promise를 사용하는 입장이다.
.then 부분을 주로 작성한다.
- 어떤 라이브러리를 봤을 때 이렇다면... 저 빨간 박스가 Promise 객체라고 생각하면 된다.
<test07>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test07.jsp</title>
</head>
<body>
<script>
//가상의 주어진 작업을 처리 하는 함수 (주어진 작업을 처리하는데 random 한 시간이 걸린다고 가정하자)
function work(job, callback){
//0 ~ 5000 사이의 랜덤한 숫자 얻어내기
let ranTime=Math.random()*5000;
// 첫번째 인자로 전달된 함수를 random 한 이후에 호출하기
setTimeout(function(){
console.log(job+" 을(를) 수행 했습니다.");
callback();
}, ranTime);
}
//어떤 작업을 시작하고 Promise 객체를 리턴하는 함수
function startWork(job){
//Promise 객체를 생성해서
let p=new Promise(function(resolve){
work(job, resolve);
});
//리턴해주기
return p;
}
startWork("달리기")
.then(function(){
return startWork("쉬기");
})
.then(function(){
return startWork("잠자기");
})
.then(function(){
console.log("모든 작업을 순서대로 마쳤습니다.");
});
/*
work("달리기", function(){
work("쉬기", function(){
work("잠자기", function(){
console.log("모든 작업을 순서대로 마쳤습니다.");
});
});
});
*/
</script>
</body>
</html>
- 특별한 Promise 를 리턴하는 함수를 누군가 만들어놔서 그것을 사용하는 입장인 것!
- 주로 .then의 내용을 작성하게 된다.
- then 안의 함수는 즉시 호출된다. / resolve는 일정 시간 이후에 호출된다.(앞의 일이 처리되고 나서)
- promise → resolve → then → promise → resolve → then → ... 이런 구조!
- 이런 코드를 보면 fetch라는 함수가 리턴해주는 무언가는 promise 객체라는 것을 알 수 있다.
- 아래 달린 .then을 보면 Promise 함수인 것을 알 수 있다.
- 주로 누군가가, 무언가가 리턴해주는 promise를 사용한다. 모양을 보고 promise 라는 것을 알면 된다.
<test08>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test08.jsp</title>
</head>
<body>
<script>
/*
Promise 는
언제 끝날지 모르는 불확실한 비동기 작업을
콜백 지옥에 빠지지 않고
순서대로 수행하고자 할때 사용합니다.
- 특히 작업이 실패 되었을때 처리가 훨씬 수월 합니다.
*/
function openDoor(){
return new Promise(function(resolve){
setTimeout(function(){
console.log("1초를 소모하면서 문을 열었습니다.");
resolve();
}, 1000);
});
}
function putElephant(){
return new Promise(function(resolve){
setTimeout(function(){
console.log("3초를 소모하면서 코끼리를 집어 넣었습니다.");
resolve();
}, 3000);
});
}
function closeDoor(){
return new Promise(function(resolve){
setTimeout(function(){
console.log("2초를 소모하면서 문을 닫았습니다.");
resolve();
}, 2000);
});
}
openDoor()
.then(function(){
return putElephant();
})
.then(function(){
return closeDoor();
})
.then(function(){
console.log("코끼리를 성공적으로 냉장고에 넣었습니다.");
});
</script>
</body>
</html>
- 밑줄이 그어진 함수 전부 promise 타입 객체이다.
- promise타입이 리턴되어야만 then이 달릴 수 있다.
- 안에서 리턴되는 것도 모두 promise 타입이다. 모양을 익히기!
<test09>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>/promise/test09.jsp</title>
</head>
<body>
<h1>fetch() 함수 테스트</h1>
<button id="myBtn">눌러보셈</button>
<a href="${pageContext.request.contextPath }/index.jsp">눌러보셈</a>
<script>
document.querySelector("#myBtn").addEventListener("click", function(){
//페이지 전환 없이 index.jsp 페이지로 요청하기
fetch("${pageContext.request.contextPath }/index.jsp")
.then(function(response){
return response.text();
})
.then(function(data){
//index.jsp 페이지가 응답하는 문자열이 함수의 인자로 전달된다.
console.log(data);
});
});
</script>
</body>
</html>
- 추출한 문자열을 javascript로 받을 수 있다.
- promise 기능을 사용해 페이지 전환 없이 프로필 사진을 업로드하는 기능을 구현할 수 있다.
'국비교육(22-23)' 카테고리의 다른 글
34일차(2)/jsp(20) : 회원 프로필 이미지 등록, 수정 기능 구현 (0) | 2022.11.25 |
---|---|
34일차(1)/jsp(19) : fetch, ajax 함수 (0) | 2022.11.24 |
33일차(3)/jsp(18) : 자료실 게시판 페이징 처리 (0) | 2022.11.24 |
33일차(2)/jsp(17) : 자료실 파일 삭제 기능 구현, GIT 연습 (0) | 2022.11.24 |
33일차(1)/jsp(16) : 자료실, 파일 업로드/다운로드 코드 리뷰 (0) | 2022.11.23 |