220v
젝무의 개발새발
220v
전체 방문자
오늘
어제
  • 분류 전체보기 (255)
    • AI (35)
      • ML, DL 학습 (30)
      • 논문 리뷰 (4)
      • 실습 및 프로젝트 (1)
    • Algorithm (145)
      • LeetCode (13)
      • 프로그래머스 (35)
      • 백준 (96)
      • 알고리즘, 문법 정리 (1)
    • Mobile, Application (17)
      • Flutter (10)
      • iOS, MacOS (7)
    • BackEnd (7)
      • Flask (1)
      • Node.js (5)
      • Spring, JSP..etc (1)
    • Web - FrontEnd (18)
      • JavaScript, JQuery, HTML, C.. (12)
      • React (6)
    • DataBase (1)
      • MySQL (1)
      • Firebase Firestore (0)
      • Supabase (0)
    • Git (1)
    • 기타 툴 및 오류 해결 (3)
    • 강의 (5)
      • Database (3)
      • 암호학 (2)
      • 알고리즘 (0)
    • 후기와 회고 (2)
    • 블로그 꾸미기 (1)
    • 일상과 이것저것 (20)
      • 맛집 (12)
      • 세상사는일 (4)
      • 도서리뷰 (1)
      • 이런저런 생각들 (잡글) (3)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 오블완
  • BFS
  • bitmasking
  • 다익스트라
  • dfs
  • disjoint set
  • 백준
  • 구현
  • Backtracking
  • topological sort
  • binary search
  • Mathematics
  • REACT
  • Greedy
  • 프로그래머스
  • Priority Queue
  • Prefix Sum
  • brute-Force
  • 위상 정렬
  • Minimum Spanning Tree
  • 티스토리챌린지
  • top-down
  • union-find
  • implementation
  • Lis
  • two pointer
  • IMPLEMENT
  • Dynamic Programming
  • simulation
  • dp

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
220v

젝무의 개발새발

Web - FrontEnd/JavaScript, JQuery, HTML, CSS..

[JavaScript] JavaScript 기본 문법 (9) - Javascript의 비동기 처리 (2) - Promise

2022. 4. 25. 00:51

JavaScript 비동기 처리 시리즈

  • [JavaScript] JavaScript 기본 문법 (8) - Javascript의 비동기 처리 (1) - 콜백(Callback)
  • [JavaScript] JavaScript 기본 문법 (10) - Javascript의 비동기 처리 (3) - async/await

 

Promise 기본

Promise 객체의 기본 형태와 resolve, reject 콜백

let promise = new Promise(function (resolve, reject) {
    // code
});

Promise 객체는 이런 방식으로 만들 수 있다.

new Promise() 에 인자로 전달되는 함수(function (resolve, reject){ })는 executor 라고 부른다.

executor 는 new Promise() 로 Promise 객체가 만들어질 때 자동으로 실행된다.

executor 의 인수 resolve, reject 는 JavaScript에서 자체 제공하는 콜백(Callback)이다.

executor 내에서 인수로 넘겨준 콜백 resolve, reject 중 하나를 반드시 호출해줘야 한다.

  • resolve("value") - 작업 성공 시 결과값을 나타내는 "value" 와 함께 호출
  • reject("error") - 에러 발생 시 에러 객체 "error" 와 함께 호출

정리하자면, executor 는 Promise 객체가 생성될 때 자동으로 실행되며, 그 성공 여부에 따라 resolve 또는 reject를 호출해주면 된다.

 

Promise 객체의 내부 property - state, result

Promise 객체는 state 와 result 라는 내부 property(프로퍼티)를 갖는다.

State

state는 Promise 객체의 처리 상태에 따라 3가지 상태를 갖는다.

  • Pending - 비동기 처리가 완료되지 않은 상태.
  • Fulfilled - resolve() 가 호출되어 "value" 를 반환한 상태.
  • Rejected - reject() 가 호출되어 처리에 실패하거나 "error" 를 반환한 상태.

Result

Result도 마찬가지로 Promise 객체의 state에 따라,

  • pending 상태일 때는 undefined
  • resolve("value") 가 호출되었을 때는 "value"
  • reject("error") 가 호출되었을 때는 "error"

의 값을 갖는다.

 

let promise = new Promise(function (resolve, reject) {
    setTimeout(() => {
        const value = 1;
        resolve(value);
    }, 1000);
});
/* 1초 후, Promise 객체의 state - fulfilled, result - 1 */
let promise = new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject(new Error("Error Msg"));
    }, 1000);
});
/* 1초 후, Promise 객체의 state - rejected, result - Error : Error Msg */

 

 

then, catch, finally

Promise 객체를 생성하고 executor가 result 로 value 또는 error 를 뱉어낸다.

그렇다면, 그놈들을 이제 받아서 처리할 아이들이 있어야 할 것이 아닌가?

그게 바로 .then(), .catch(), .finally() 다.

이 아이들은, 함수를 인자로 받는다!

 

then

Usage

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

then은 두 가지 인자를 받는다.

첫 번째 인자는 Promise 객체가 resolve 되었을 때 실행되는 함수,

두 번째 인자는 Promise 객체가 reject 되었을 때 실행되는 함수.

물론 당연히,

첫 번째 함수의 인자 (위 Usage에서 function(result) 의 result) 는 Promise 객체에서 resolve() 로 반환한 value이고,

두 번째 함수의 인자 (위 Usage에서 function(error) 의 error) 는 Promise 객체에서 reject() 로 반환한 error이다.

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

promise.then(
    (value) => { console.log(value); },
    (error) => { console.log(error); }
);
// output : (Promise resolved!)
let promise = new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject(new Error("Promise rejected!"));
    }, 1000);
});

promise.then(
    (value) => { console.log(value); },
    (error) => { console.log(error); }
);
// output : (Error: Promise rejected!)

첫 번째 코드는 resolve되었을 때, 두 번째 코드는 reject되었을 때의 예시이다.

이런 느낌의 흐름으로 resolve에서 뱉어낸 result가 흘러간다. 물론 error의 경우도 똑같음.

이렇게 then 하나로 Promise 객체가 resolve, reject되었을 때 모두 처리할 수 있지만, 이를 추천하지 않는다.

이에 대해서는 아래에서 다시 서술한다.

 

catch

Usage

promise.catch(
  function(error) { /* 에러(error)를 다룹니다 */ }
);

catch는 then과 다르게, error가 발생한 경우(Promise 객체가 reject되었을 때 + etc..) 에 실행될 함수만을 인자로 갖는다.

let promise = new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject(new Error("An Error Occurred!"));
    }, 1000);
});

promise.catch((error) => {
    console.log(error);
});
/* output : 
Error: An Error Occurred! */

기본적으로 then에서 두 번째 인자만 존재한다고 보면 거의 비슷하게 작동한다.

 

Promise의 Error Handling은 가급적 catch로

위에서 보았듯이, .then() 을 이용해 성공과 실패 모두 처리할 수 있다.

그러나 가급적이면 .then() 으로 성공 시(resolve) 처리를, .catch() 로 실패 시(reject) 에러 처리를 하는 것을 추천한다.

그 이유는 아래에서 볼 수 있는데,

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

promise.then(
    (value) => {
        throw new Error("Error Occurred!");
    },
    (error) => {
        console.log(error);
    }
);

와 같이 하나의 then으로 resolve와 reject를 모두 처리하게 되면, then 내에서 발생하는 error에 대해서는 처리하지 못하는 문제가 발생한다.

이를 catch를 이용한 코드로 바꿔준다면, 깔끔하게 처리가 가능하다.

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

promise
    .then((value) => {
        throw new Error("Error Occurred!");
    })
    .catch((error) => {
        console.log(error);
    });
/* output : 
Error: Error Occurred! */

 

finally

try - catch 문법과 유사하게, Promise에도 finally 가 존재한다.

물론 처리되는 과정도 유사하게, Promise가 resolve 되든 reject 되든 상관없이 Promise가 처리되기만 하면 finally 를 수행한다.

Usage

new Promise(function (resolve, reject) {
    resolve("Promise Resolved!");
})
    .finally(() => {
        console.log("Promise Finish!");
    })
    .then((value) => {
        console.log(value);
    });

/* output :
Promise Finish!
Promise Resolved!
*/

new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject(new Error("An Error Occurred!"));
    }, 1000);
})
    .finally(() => {
        console.log("Promise Finish!");
    })
    .catch((error) => {
        console.log(error);
    });

/* output :
Promise Finish!
Error: An Error Occurred!
*/

 

 

Promise Chaining(Promise 체이닝)

Promise Chaining

.then() 에서는 Promise 객체 가 return된다.

따라서, 반환된 Promise 객체에 다시 .then() 을 붙여 처리할 수 있다. 이를 Promise Chaining 이라 한다.

.then() 에서 Promise 객체가 아닌 값을 return할 때엔, 그 값을 result 값으로 가진 Promise 객체가 return된다.

new Promise((resolve, reject) => {
    const value = 1;
    resolve(value);
})
	// Promise 객체를 return하여 다음 then으로 넘겨줌!
	// return하는 Promise 객체의 result값은 value*2인 "2"
    .then((value) => {
        return new Promise((resolve, reject) => {
            console.log(value);
            resolve(value * 2);
        });
    })
	// Promise 객체가 아닌 다른 값을 return할 때는
	// result값이 value*2 ("4")인 Promise 객체를 return.
    .then((value) => {
        console.log(value);
        return value * 2;
    })
    .then((value) => {
        console.log(value);
    });
/* output :
1
2
4
*/

line 8 에서처럼 then이 Promise 객체를 직접 return할 수도 있고,

line 17 에서처럼 then이 특정 값을 return하면 그 값이 Promise 객체의 result값이 되어 return된다!

 

Error Handling

Promise가 reject 되면 제일 가까운 reject handler(.catch() 와 같은..)로 넘어간다.

따라서 Promise chaining을 이용하면, 에러를 쉽게 처리할 수 있다.

new Promise((resolve, reject) => {
    const value = 1;
    resolve(value);
})
    .then((value) => {
        console.log(value);
        return value * 2;
    })
    .then((value) => {
        console.log(value);
        return new Error("Error in Promise Chain");
    })
    .then((value) => {
        console.log(value);
        return value * 2;
    })
    .catch((error) => {
        console.log(error);
    });
/* output :
1
2
Error: Error in Promise Chain
*/

line 11 에서 throw한 Error를 line 17에서 catch하여 처리함. line 13의 3번째 then은 무시된다.

이런 식으로 진행된다!

 

물론 한 번의 catch 이후 다시 error를 catch하는 것도 가능하다!

new Promise((resolve, reject) => {
    const value = 1;
    resolve(value);
})
    .then((value) => {
        console.log(value);
        THIS_IS_NOT_A_FUNCTION();	
    // 꼭 Error 객체를 throw, return하는 것이 아니더라도 모든 종류의 error를 catch한다.
    // line 15에서 catch
    })
    .then((value) => {	// 무시되는 then
        console.log(value);
        return value * 2;
    })
    .catch((error) => {
        console.log(error);
        return new Error("2nd Error");
    // line 23에서 catch
    })
    .then((value) => {	// 무시되는 then
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    });
/* output :
1
ReferenceError: THIS_IS_NOT_A_FUNCTION is not defined
Error: 2nd Error
*/

line 7 처럼 Error 객체를 throw / return하지 않더라도 모든 종류의 error를 handling한다.

 

 

Promise API

Promise Class에는 5가지 static method가 존재한다.

Promise.all

Usage

let promise = Promise.all([...promises...]);

Promise의 배열을 받아 새로운 Promise를 반환한다.

배열 안의 모든 Promise가 처리되면, 그 Promise들의 result를 담은 배열을 만들어 새로운 Promise의 result값으로 만든다.

다만, Promise 배열 중 하나라도 reject되면, Promise.all 이 반환하는 새로운 Promise는 reject된다.

let promise = Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 2000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(2);
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    }),
]);

promise.then((value) => {
    console.log(value);
});

// output : [ 1, 2, 3 ]

모든 Promise가 resolve되었을 때.

result들의 배열이 새로운 Promise의 result로 반환된다.

 

let promise = Promise.all([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 2000);
    }),
    new Promise((resolve, reject) => {	// 얘만 reject!
        setTimeout(() => {
            reject(new Error("Error in 2nd Promise"));
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    }),
]);

promise
    .then((value) => {
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    });

// output : 
// Error: Error in 2nd Promise

두 번째 Promise만 reject되었을 때.

하나라도 reject되면 새롭게 만들어지는 Promise는 reject처리된다.

 

Promise.allSettled

Promise.all 은 하나라도 reject되면 전체가 reject되지만,

Promise.allSettled 는 모든 Promise를 우선 처리하고, 모든 Promise 객체의 배열을 반환한다.

  • 응답이 성공할 경우 – {status:"fulfilled", value:result}
  • 에러가 발생한 경우 – {status:"rejected", reason:error}
let promise = Promise.allSettled([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 2000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error("Error in 2nd Promise"));
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 3000);
    }),
]);

promise
    .then((value) => {
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    });

/* output :
[
  { status: 'fulfilled', value: 1 },
  {
    status: 'rejected',
    reason: Error: Error in 2nd Promise
  },
  { status: 'fulfilled', value: 3 }
]
*/

 

Promise.race

Promise.race 는 Promise들 중 가장 먼저 처리되는 Promise의 result 또는 error 를 result / error 로 가진다.

let promise = Promise.race([
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(1);
        }, 2000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error("Error in 2nd Promise"));
        }, 1000);
    }),
    new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(3);
        }, 500);
    }),
]);

promise
    .then((value) => {
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    });

// output : 3

3번째 Promise가 0.5초 이후 가장 빨리 결과를 반환하므로, 3번째 Promise의 result인 "3" 이 Promise.race에서 반환하는 Promise 객체의 result가 된다.

 

Promise.resolve / Promise.reject

let promise = Promise.resolve(1);

promise.then((value) => {
    console.log(value);
});

// output : 1
let promise = Promise.reject(new Error());

promise.catch((error) => {
    console.log(error);
});

// output : Error

Promise.resolve는 resolve된 Promise 객체를,

Promise.reject는 reject된 Promise 객체를 반환한다.

다음 글에서 다룰 async/await 등장 이후 쓸모가 없어졌기 때문에, 간략하게 넘어가도록 하겠다.

 

<= [JavaScript] JavaScript 기본 문법 (8) - Javascript의 비동기 처리 (1) - 콜백(Callback)

=> [JavaScript] JavaScript 기본 문법 (10) - Javascript의 비동기 처리 (3) - async/await

 

    'Web - FrontEnd/JavaScript, JQuery, HTML, CSS..' 카테고리의 다른 글
    • [HTML/CSS] 마우스 오버 시 커서 스타일 변경
    • [JavaScript] JavaScript 기본 문법 (10) - Javascript의 비동기 처리 (3) - async / await
    • [JavaScript] JavaScript 기본 문법 (8) - Javascript의 비동기 처리 (1) - 콜백(Callback)
    • [JavaScript] JavaScript 기본 문법 (7) - 객체 (4) - new 연산자와 생성자
    220v
    220v
    DGU CSE 20 / Apple Developer Academy @ POSTECH 2nd Jr.Learner.

    티스토리툴바