Promise
ex)
let myFirstPromise = new Promise((resolve, reject) => { // 하단의 비동기 작업이 성공한 경우 resolve(...)를 호출하고, 실패한 경우 reject(...)를 호출 // 여기서는 setTimeout()을 사용해 비동기 코드를 흉내, 실제로는 여기서 XHR이나 HTML5 API를 사용 setTimeout(function () { resolve("성공!"); // 와! 문제 없음! }, 250); }); myFirstPromise.then((successMessage) => { // successMessage : 위에서 resolve(...) 호출에 제공한 값 console.log("와! " + successMessage); });
- 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냄
- 비동기 메서드에서 마치 동기 메서드처럼 값을 반환
- 최종 결과x, 미래의 결과에 대한 약속을 반환
- callback hell에서 어느정도 해방 가능
- chaining 방식(.then().then()…)으로 1depth
- 상태
- 대기(pending): 이행하지도, 거부하지도 않은 초기 상태.
- 이행(fulfilled): 연산이 성공적으로 완료됨.
- 거부(rejected): 연산이 실패함.
- 이행이나 거부될 때, 프로미스의
then
메서드에 의해 대기열(큐)에 추가된 handler들이 호출됨
- 생성 :
new Promise(executor)
- executor :
(resolve, reject) ⇒ {}
형태 - 프로미스를 지원하지 않는 함수를 감쌀 때 사용
- 실행 함수의 반환값은 무시됨
- 어떤 비동기 작업을 시작한 후
- 모든 작업을 성공적으로 끝내면 인자에 반환 값을 넣은
resolve
를 호출해 프로미스를 이행 - 오류가 발생한 경우 인자에 반환 값을 넣은
reject
를 호출해 거부
정적 메서드
resolve(value)
: 주어진 값으로 이행하는Promise
를 반환- 어떤 값이 프로미스인지 아닌지 알 수 없는 경우, 보통 일일히 두 경우를 나눠서 처리하는 대신
Promise.resolve()
로 값을 감싸서 항상 프로미스가 되도록 만든 후 작업하는 것이 좋다.
ex)
const cahced = { 'roto' : 'bassist' } const findMember = (memberName) => { //항상 프로미스를 반환 if (chached[memberName]) { return Promise.resolve(chached[memberName]) //프로미스로 만듦 } return request('http~') .then((member)=> { cache[memberName] = member.job return member.job //해당 프로미스의 결과값을 이걸로 변경 } } findMember('roto').then((job) => console.log(job))
reject(reason)
: 주어진 사유로 거부하는Promise
를 반환- 강제로 reject해야할 때 사용
all(프로미스배열)
: 배열 안에 있는 모든 프로미스가 다 resolve되거나, 한 프로미스가 reject될 때까지 대기하는 새로운 프로미스를 반환- 모두 reolve된다면, 각 resolve 값 순서대로 모두 모아놓은 배열이 then handler의 매개변수로 제공
- 한개라도 reject되면 첫 reject된 프로미스 사유가 catch handler의 매개변수로 제공
allSettled(프로미스배열)
: 모든 프로미스가 resolve/reject 됐을 경우 프로미스를 반환- 각 결과(resolve 값이나 reject 사유)를 순서대로 모두 모아놓은 배열이 then handler의 매개변수로 제공
race(프로미스배열)
: 모든 프로미스 중 첫번째로 resolve/reject된 프로미스를 반환
any(프로미스배열)
: 모든 프로미스 중 첫번째로 resolve된 프로미스를 반환
인스턴스 메서드
then
: resolve의 handler를 추가- handler의 매개변수는 promise의 이행 결과값이다
- 반환 값이 있다면, 해당 promise의 결과 값이 이 반환 값으로 변경된다.
- 호출되지 않는 경우, 상태에 맞는 값과 콜백을 가진 새로운 프로미스를 반환
catch
: reject의 handler 추가- 호출되지 않는 경우(resolve된 경우), resolve 값과 콜백을 가진 새로운 프로미스를 반환
- 넣지 않을 경우 promise chain이 멈추므로 가급적으로 넣는 것이 좋음
finally
: 여부에 상관없이 처리될 경우 항상 호출되는 처리기 콜백- 새로운 프로미스를 반환
- .then으로 비동기 함수들을 chaining할 수 있음
ex)
const delay = (delayTime) => new Promise((resolve) => { setTimeout(resolve, delayTime) }) delay(5000) .then(() => { return delay(3000) }).then(() => { console.log('complete') });
<api.js를 promise로 바꿔보기>
전) App.js에서 콜백함수 → api.js로 넘겨서 그 함수에 data를 인자로 넣어 사용
후) api.js에서 data를 resolve의 결과 값 → App.js로 넘겨서 data 사용
- api.js : resolve/reject 알려줌
- App.js : then, catch 처리
api.js
- 이제 콜백함수를 넘겨받지 않음.
export function request(url) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.addEventListener("load", (e) => { if (xhr.readyState === 4) { if (xhr.status === 200) { resolve(JSON.parse(xhr.responseText)); } else { reject(xhr.statusText); } } }) xhr.addEventListener('error', (e) => reject(xhr.statusText)) xhr.open('GET', url) xhr.send() }) }
App.js
- then으로 resolve handler를 추가
- 콜백함수의 매개변수인 data는 promise의 이행 결과값이다
//댓글 목록 불러오기 request(`https://kdt-frontend.programmers.co.kr/comments?todo.id=${id}`) .then((data) => { this.setState({ ...this.state, comments: data }) }); ... //todos 불러오기 this.init = () => { request("https://kdt-frontend.programmers.co.kr/todos") .then((data) => { this.setState({ ...this.state, todos : data }) }) }