JS. Promise 함수

MJ·2023년 6월 8일
0
post-thumbnail

Promise 함수

  • 값이나 오류에 대한 최종 약속, 좋든 나쁘든 무언가의 최종 결과(값) 입니다.

  • Promise 함수의 상태는 크게 세가지로 나뉩니다
    pending : 대기상태 | rejected : 실패 | resolved : 성공

  • Promise 함수의 상태가 rejected 또는 resolved때 실행 됩니다.

  • resolved 상태가 되면 .then 에 반환값이 전달되고, rejected 상태가 되면
    .catch에 반환 값이 전달 됩니다.

함수안에 콜백을 전달하는 것이 아니라, Promise 객체 자체에 콜백을 첨부해야
합니다. 그리고 함수가 Promise 객체를 반환하기를 기다리면 됩니다.


1.1 Promise 세가지 상태

  • new Promise 에 전달되는 함수는 executor(실행자, 실행함수)라고 부릅니다.
    executor은 Promise 객체가 만들어 질 때 자동으로 실행되는데, 결과를 최종적으로
    만들어내는 제작코드를 포함한다.

  • executor의 인수 resolvrejectJS에서 제공하는 콜백
    resolv(value) : 작업이 성공적으로 끝난 경우 결과를 나타내는 value와 함께 호출
    reject(value) : 작업 실패, 또는 에러 발생 시 에러 객체를 나타내는 value와 함께 호출

Promise 내부 동작 원리

new Promise 생성자가 반환하는 promise 객체는 다음과 같은 내부 프로퍼티를 갖는다.

state
처음엔 pending(보류)상태 였다가 resolv가 호출되면 fulfilled, reject가 호출되면
rejected로 변합니다.

result
처음엔 undefined상태 였다가 resolv(value)가 호출되면 value로, reject(error)
이 호출되면 error로 변합니다.

제작 코드(producing code)?
제작 코드는 원격에서 스크립트를 불러오는 것 같은 시간이 걸리는 일을 합니다


1.2 Promise 함수 호출하기

/* promise state resolv */

let promise = new Promise(function(resolve, reject) {
  // 프라미스가 만들어지면 executor 함수는 자동으로 실행됩니다.

  // 1초 뒤에 일이 성공적으로 끝났다는 신호가 전달되면서 result는 '완료'가 됩니다.
  setTimeout(() => resolve("완료"), 1000);
});

/*
1. 생성자 함수는 new promise 에 의해 자동으로 호출됩니다.
2. 생성자 함수는 resolv와 reject를 인자로 받습니다. ( 둘 중 하나는 호출해야 한다 )
3. 내부 콜백함수를 통해 resolv('완료') 함수가 1초뒤에 호출되고 결과가 출력됩니다
*/

resolv 결과


/* promise state reject */

let promise = new Promise(function(resolve, reject) {
  // 1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
  setTimeout(() => reject(new Error("에러 발생!")), 1000);
});

/* 
1. 내부 콜백함수를 통해 reject(..~) 함수가 1초뒤에 호출되고 결과가 출력됩니다.
*/

reject 결과


1.3 Promise 객체의 메서드들

1.3.1 .then 메서드

/* .then 메서드 */

promise.then(
  function(result) { /* 결과(result)를 다룹니다 */ },
  function(error) { /* 에러(error)를 다룹니다 */ }
);

/*
1. .then의 첫 번째 인수는 promise 작업이 성공적일 때 실행되는 함수로서 실행결과를 받습니다.
2. .then의 두 번째 인수는 promise 작업이 실패했을 때 실행되는 함수로 에러를 받습니다.
*/
/* resolv 상태 */

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve("완료!"), 1000);
});

// resolve 함수는 .then의 첫 번째 함수(인수)를 실행합니다.
promise.then(
  result => alert(result), // 1초 후 "완료!"를 출력
  error => alert(error) // 실행되지 않음
);
/* reject 상태 */

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => reject(new Error("에러 발생!")), 1000);
});

// reject 함수는 .then의 두 번째 함수를 실행합니다.
promise.then(
  result => alert(result), // 실행되지 않음
  error => alert(error) // 1초 후 "Error: 에러 발생!"을 출력
);

1.3.2 catch 메서드

/* .catch */

let promise = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error("에러 발생!")), 1000);
});

// .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력

/* 
에러가 발생한 경우만 다루고 싶다면 .then 메서드를 사용해서 2번째 매개변수에 에러처리에 관한 구문을
작성해도 됩니다. then 메서드의 2번째 매겨변수는 catch 메서드와 동일하게 작동합니다.
.then(null, error) === .catch(), then 메서드의 error 매개변수와 동일한 역할
*/

1.4 Promise State 확인

  • 호출 과정은 브라우저에서 진행 합니다.
new Promise((resolve, reject) => {
    resolve();
})

/* 
첫 번째 인수 reseolv() 호출 

[[Prototype]] : Promise
[[PromiseState]] : "fulfilled"
[[PromiseResult]] : undefined

첫 번째 인수를 호출하면 PromisState가 fullfilled(이행상태)가 된다.
이행 상태는 성공을 뜻합니다.
*/
new Promise((resolve, reject) => {
    reject();
})

/* 
두 번째 인수 reject() 호출

[[Prototype]] :  Promise
[[PromiseState]] : "rejected"
[[PromiseResult]] : undefined

두 번째 인수를 호출하면 PromisState가 rejected(거절)가 된다.
거절 상태는 오류가 발생하거나 실패를 뜻합니다.
*/
new Promise((resolve, reject) => {
   
})

/* 
인수를 호출하지 않음 

[[Prototype]] : Promise
[[PromiseState]] : "pending"
[[PromiseResult]] : undefined

인수를 호출하지 않으면 Pending 상태가 되며 대기상태입니다.
비동기에 대한 로직을 기다리고 있는 상태.
*/

1.5 .then 메서드를 사용한 중첩 함수

const fakeRequest = (url) => {
    return new Promise((resolve, reject) => {
        const random = Math.floor(Math.random() * 10) + 1
        setTimeout(() => {
            if (random < 6) {
                resolve(`${url} 요청 완료! 접속 성공`);
            } else {
                reject('요청 거부※ 접속 실패!!!');
            }
        }, 1000)
    })
}

fakeRequest('/dogs/1')
    .then((data) => {
        console.log('서버에 접속을 요청합니다.');
        console.log(data);
        fakeRequest('/dogs/2')
            .then((data) => {	// 2차 응답 (성공)
                console.log('서버에 접속을 요청합니다. (2nd)')
                console.log(data)
            })
            .catch((err) => {	// 2차 응답 (거절)
                console.log('ON NO!!', err)
            })
    })
    .catch((err) => {
        console.log('Oh No!!', err)
    })

/* 
fakeRequest 호출 과정
	1. 함수를 호출하고, 함수의 반환 값(resolv, reject)를 .then 구조가 받고 다음 
    구문을 실행
    
    2. 또한 코드의 흐름 마지막 줄에 .catch 을 한 가지만 작성해도 어떤 함수던 호출 이후
    reject을 반환한다면 바로 catch 구문으로 이동하여 오류 부분의 구문을 출력합니다.
    
    3. .then 메서드 안에 중첩함수를 사용해서 1차응답 이외에도 2차응답을 받기위해
    함수를 재 호출합니다. 
    
    
fakeRequest 함수
	1. new Promise에 전달되는 함수를 executor(실행 함수)라고 부릅니다.
    Promise 함수가 만들어 질 때 자동으로 실행되며, 결과를 최종적으로 만들어내는
    제작코드를 포함한다. 그리고 해당 함수를 return 합니다.
    
    2. executor의 인수 resolv와 reject는 JS에서 자체로 제공하는 콜백함수
    resolv : Promise가 성공적으로 이행(fulfilled)됏을 때 반환값을 .then 구문에 전달
    reject : Rromise가 거절된경우 reject 반환 값을 .catch 구문에 전달
*/

1.6 중첩 함수 없이 Promise 사용하기

  • .then 메서드 안에서 함수를 호출하면서 반환값을 return 하면, .then 메서드에
    반환값이 전달되므로, 2-3차 응답에 관한 함수를 중첩해서 사용할 필요가 없습니다.
const fakeRequest = (url) => {
    return new Promise((resolve, reject) => {
        const random = Math.floor(Math.random() * 10) + 1
        setTimeout(() => {
            if (random < 6) {
                resolve(`${url} 요청 완료! 접속 성공`);
            } else {
                reject('요청 거부※ 접속 실패!!!');
            }
        }, 1000)
    })
}


/* 함수의 반환 값(resolv or reject)를 .then에 반환하므로 중첩함수 없이 사용 가능 */
fakeRequest('/dogs/1')
    .then((data) => {
        console.log(`서버에 접속을 요청합니다 >>> ${data}`);
        return fakeRequest('/dogs/2')
    })
    .then((data) => {
        console.log(`서버에 접속을 요청합니다 >>>  ${data}`);
        return fakeRequest('/dogs/3')
    })
    .then((data) => {
       return console.log(`서버에 접속을 요청합니다 >>>  ${data}`);
  		// .then 메서드가 더이상 없기에 마지막 구문에서는 return 하지 않아도 무방합니다 
    })
    .catch((err) => {
        console.log('Oh No!!', err)
    })


/* 
동작 원리

1. 전역 공간에서 fakeRequest('/dogs/1') 함수 호출하면서 인수 전달
2. fakeRequest 함수 표현식에서 매개변수(url)에 전달된 인수를 저장 ( url = '/dogs/1' )
3. new 연산자로 Promise() 객체 생성, 생성하면서 만들어지는 생성자 함수 (resolv,reject) => { }
함수를 생성 ( 해당 함수는 Promise() 에게 값을 전달합니다 )

4. 내부의 콜백함수(setTimeout)에서 난수변수(random)을 통해 조건을 비교
참인 경우에는 resolv() 함수를 호출하고 거짓인 경우에는 reject() 함수를 호출한다.

5. 참이라고 가정하고, resolv('~~~') 함수를 호출하고 문자열을 인수로 전달한다. 이 결과
값은 new Promise 객체에 최종적으로 전달합니다.

6. new Promise 객체는 자신이 전달받은 결과 값(resolv)를 return 합니다.
** Promise 객체를 반환하지 않으면, 전역 공간에서 .then 메서드에 접근할 수 없다 **

7. resolv()가 반환 됬기에  Promise.then 메서드에 접근할 수 있게되며, 첫 번째 .then
메서드에 접근할 수 있게 된다.

8. 콘솔로그를 출력하고 fakeRequest('/dogs/2') 함수를 호출한다. 
해당 함수의 결과 값(resolv or reject)을 .then 메서드에 return 해줍니다.
** 함수를 return을 해주지 않으면, 반환값이 promise.then에 전달되지 않기에 
다음 구문에서 .then 메서드안의 data 인수는 undefined 상태가 된다. **

9. Promise 객체의 반환 값이 reject인 경우에만 .catch 메서드로 이동해서 실패에 대한
에러를 처리합니다.

10. 결과적으로 Promise 객체 하위에 .then 또는 .catch 메서드에 반환 값이 저장되어
함수를 중첩으로 사용할 이유가 없습니다.
*/

함수를 return 해줘야 하는 이유

.then 메서드 내부에서 함수를 호출하고 return 해주지 않으면 함수의 반환값이
promise 객체에 전달되지 않아서 현재 작업이 완료되지 않고 다음 .then 구문으로
넘어가게 됩니다.

즉, WebAPI 에서 값을 가져오지 못하고 다음 .then구문으로 넘어가기에 다음 메서드
내부에서는 이전의 함수에 결과 값(resolv)를 전달받지 못해서 해당 값은 undefined 상태
가 됩니다.

즉, return을 사용하면 현재 함수의 결과 값이 promise 객체에 전달되기 전 까지 다음
구문으로 넘어가지 않습니다. 그렇기에 동시에 작업이 수행되지도 않고 다음 .then
메서드 내부에서는 이전 함수의 결과 값도 제대로된 값으로 전달받을 수 있습니다.


1.7 Promise 객체를 사용해서 배경색 변경하기

  • 이전 포스팅에서 배경색상을 변경하는 중첩함수의 코드를 Promise 객체가 참조할 수
    있는 코드로 수정해서 실습을 진행합니다.
function dealyChangeColor(color, delay) {
    return new Promise(function (resolv, reject) {
        setTimeout(() => {
            document.body.style.backgroundColor = color;
            resolv()
        }, delay)
    })

}

dealyChangeColor('red', 1000)
    .then(() => {
        return dealyChangeColor('yellow', 1000)
    })
    .then(() => {
        dealyChangeColor('green', 1000)
    })

/* 
동작 원리

1. dealyChangeColor 함수 호출 및 인수 전달 ('red', 1000)
2. 함수 내부 > Promise의 생성자 함수 내부 > 콜백함수에서 배경색을 delay초 후에 변경 
3. reject가 필요 없는 경우기에 resolv() 인자를 호출하고 Promise 객체에 반환한다.
4. return 지시자로 인해서 Promise 객체를 반환 
5. 반환 값이 resolv 이기에 .then 구문으로 이동 > 함수를 다시 호출하고 배경색을 변경한 후
함수의 반환 값(resolv)를 Promise 객체에 반환한다.
* 함수를 return 하지 않으면 현재 함수의 Promise가 완료되지 않은 상태로 다음 .then 메서드로
이동하기에 WEBAPI에서는 다음 함수와 동시에 처리하게 된다. delay 시간이 동일하게 1초이므로
배경색이 노란색으로 변경했다가 지연시간없이 바로 초록색으로 변경해서 육안상으로는 노란색이
스킵되어 보이지 않게 된다. *

6. 마지막 .then 구문에서 이전 Rromise 작업이 완료되면 함수를 호출하고 배경색을 변경한다.
*/

결과

profile
프론트엔드 개발자가 되기 위한 학습 과정을 정리하는 블로그

0개의 댓글