본문 바로가기

웹언어/자바스크립트 - ES6

[ES6강좌] 10강 JS Promise - 오쌤의 니가스터디

728x90
반응형

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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/

 

JSONPlaceholder - Free Fake REST API

{JSON} Placeholder Free fake API for testing and prototyping. Powered by JSON Server + LowDB. Tested with XV. Serving ~2 billion requests each month.

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>

 

 

 

## 데이터 가져오는 것이 성공했을 때

- 데이터를 가져오기 전까지는 로딩중이라고 뜨는 것이 확인됩니다.

- 데이터 가져오는 것이 성공하면 위와 같은 화면이 나옵니다. 

 

 

 

## 데이터 가져오는 것이 실패했을 때

- 위는 주소를 일부러 틀리게 해봤습니다. 그럼 파일을 찾을 수 없다고 나옵니다. 

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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 를 배워보도록 하겠습니다. 

 

 

 

## 결과 보기

- 결과는 역시 잘 나오는 것이 확인됩니다. 

 

 

 

 

 

 

 

 

 

 

728x90
반응형