3 분 소요

Promise란?

자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 오브젝트
비동기를 처리할 때 콜백함수 대신 Promise 사용 가능
🔔 State (상태)
pending: 프로미스가 만들어져서 우리가 지정한 오퍼레이션을 수행중인 상태
fulfilled: 오페레이션을 성공적으로 끝낸 상태
rejected: 파일을 찾을 수 없거나 네트워크에 문제가 생긴 상태
🔔 Producer vs Consumer
producer: 우리가 원하는 기능을 수행해서 해당하는 데이터를 만들어냄
consumer: 제공된 데이터를 소비함

Producer

우리가 원하는 기능을 비동기적으로 실행하는 producer 프로미스 만들기

const promise = new Promise((resolve, reject) => {
    // doing some heavy work (네트워크에서 데이터를 받아오거나 파일에서 데이터를 읽어옴..)
    console.log('doing something...');

    // 프로미스 안에서 네트워크 통신을 하는 것처럼 시간의 delay를 줌
    setTimeout(() => {
        resolve('ann'); //성공적으로 네트워크 혹은 파일에서 데이터를 받어왔다면 resolve()라는 콜백함수를 통해 데이터를 전달
        // reject(new Error('no network'));
    }, 2000);
});

🌟 프로미스를 만드는 순간, 우리가 전달한 executor라는 콜백함수가 바로 실행됨
만약, 프로미스 안에 네트워크 통신을 하는 코드를 작성했다면, 프로미스가 만들어지느 그 순간 네트워크 통신을 수행하게 됨
🚨 따라서 네트워크 요청을 사용자가 요구했을 때만 해야하는 경우라면 불필요한 네트워크 통신이 일어날 수 있음!

Consumer: then, catch, finally

위에서 만든 프로미스 사용하기

promise
    .then((value) => {
        // 이때 value는 프로미스가 resolve()라는 콜백함수로 전달한 데이터
        console.log(value);
    })
    .catch((error) => {
        console.log(error);
    })
    .finally(() => {
        // 성공하는 실패하든 실행됨
        console.log('finally');
    });

Promise chaining

// 서버에서 숫자를 받아오는 새로운 프로미스 생성 (실제로 서버에서 받아오지는 않고, setTimeout을 통해 가정)
const fetchNumber = new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000); // 숫자 1 전달
});

fetchNumber
    .then(num => num * 2) // 1 * 2 = 2
    .then(num => num * 3) // 2 * 3 = 6
    .then(num => {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(num - 1), 1000);  // 6 - 1 = 5
        });
    })
    .then(num => console.log(num)); // 5

Error handling

총 세가지의 프로미스를 리턴하는 함수가 있다.

// 암탉을 받아오는 프로미스
const getHen = () => 
new Promise((resolve, reject) => {
    setTimeout(() => resolve('🐓'), 1000);
});
// 암탉으로부터 달걀을 받아오는 프로미스
const getEgg = hen => 
new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${hen} => 🥚`), 1000);
});
// 달걀로부터 후라이를 요리하는 프로미스
const cook = egg => 
new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});

// getHen()
//     .then(hen => getEgg(hen))
//     .then(egg => cook(egg))
//     .then(friedEgg => console.log(friedEgg));

// 받아온 value를 바로 다음 함수에 파라미터로 전달할 때에는 아래처럼 깔끔하게 표현 가능
getHen()
    .then(getEgg)
    .then(cook)
    .then(console.log); // 🐓 => 🥚 => 🍳

암탉으로부터 달걀을 받아오다가 문제가 생긴다면 다른 재료로 대체를 하고 싶다면? (error handling)

const getHen = () => 
new Promise((resolve, reject) => {
    setTimeout(() => resolve('🐓'), 1000);
});
const getEgg = hen => 
new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error(`error! ${hen} => 🥚`)), 1000); // error 발생
});
const cook = egg => 
new Promise((resolve, reject) => {
    setTimeout(() => resolve(`${egg} => 🍳`), 1000);
});

// error handling X
getHen()
    .then(getEgg)
    .then(cook)
    .then(console.log) 
    .catch(console.log); // Error: error! 🐓 => 🥚

// error handling O
getHen()
    .then(getEgg)
    .catch(error => {
        return '🥐';
    })
    .then(cook)
    .then(console.log) // 🥐 => 🍳
    .catch(console.log);

이전에 만든 콜백지옥을 프로미스를 이용해 변경하기

😈 이전 코드

class UserStorage{
    loginUser(id, password, onSuccess, onError){
        setTimeout(() => {
            if((id === 'ann' && password === 'hello') || (id === 'coder' && password === 'academy')){
                onSuccess(id);
            }else{
                onError(new Error('not found'));
            }
        }, 2000);
    }
    
    getRoles(user, onSuccess, onError){
        setTimeout(() => {
            if(user === 'ann'){
                onSuccess({name: 'ann', role: 'admin'});
            }else{
                onError(new Error('no access'));
            }
        }, 1000);
    }
}

const userStorage = new UserStorage();
const id = prompt('Enter your id');
const password = prompt('Enter your password');
userStorage.loginUser(
    id,
    password,
    user => {
        userStorage.getRoles(
            user,
            userWithRole => {
                alert(`Hello ${userWithRole.name}, you have a ${userWithRole.role} role.`);
            },
            error => { console.log(error); }
        )
    },
    error => { console.log(error); }
);

🥺 프로미스를 사용한 코드

class UserStorage{
    loginUser(id, password){
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if((id === 'ann' && password === 'hello') || (id === 'coder' && password === 'academy')){
                    resolve(id);
                }else{
                    reject(new Error('not found'));
                }
            }, 2000);
        });
    }

    getRoles(user){
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                if(user === 'ann'){
                    resolve({name: 'ann', role: 'admin'});
                }else{
                    reject(new Error('no access'));
                }
            }, 1000);
        });
      
    }
}

const userStorage = new UserStorage();
const id = prompt('Enter your id');
const password = prompt('Enter your password');

userStorage.loginUser(id, password)
.then(userStorage.getRoles)
.then(user => alert(`Hello ${user.name}, you have a ${user.role} role.`))
.catch(console.log);

📁 참고

DreamCoding



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기