프로미스

프로미스
자바스크립트와 노드에서는 주로 비동기 프로그래밍을 합니다.
특히 이벤트 주도 방식 때문에 콜백 함수를 자주 사용합니다.
ES2015부터는 자바스크립트와 노드의 API들이 콜백 대신 프로미스(Promise) 기반으로 재구성됩니다.
그래서 악명 높은 콜백 헬(callback hell)을 극복했다는 평가를 받고 있습니다.

프로미스는 다음과 같은 규칙이 있습니다.
먼저 프로미스 객체를 생성해야 합니다.
                    
                        const condition = true; // true면 resolve, false면 reject
                        const promise = new Promise((resolve, reject) => {
                            if (condition) {
                                resolve('성공');
                            } else {
                                reject('실패');
                            }
                        });

                        promise
                            .then((message) => {
                                console.log(message); // 성공(resoleve)한 경우 실행
                            })
                            .catch((error) => {
                                console.error(error); // 실패(reject)한 경우 실행
                            })
                    
                
new Promise로 프로미스를 생성할 수 있으며, 안에 resolve와 reject를 매개변수로 갖는 콜백함수를 넣어줍니다.
이렇게 만든 promise 변수에 then과 catch 메서드를 붙일 수 있습니다.
프로미스 내부에서 resolve가 호출되면 then이 실행되고,
reject가 호출되면 catch가 실행됩니다.

resolve와 reject에 넣어준 인자는 각각 then과 catch의 매개변수에서 받을 수 있습니다.
즉, resolve('성공')가 호출되면 then의 message가 '성공'이 됩니다.
만약 reject('실패')가 호출되면 catch의 error가 '실패'가 되는 것입니다.
condition 변수를 false로 바꿔보면 catch에서 에러가 로깅됩니다.

then이나 catch에서 다시 다른 then이나 catch를 붙일 수 있습니다.
이전 then의 return 값을 다음 then의 매개변수로 넘깁니다.
프로미스를 return 한 경우 프로미스가 수행된 후 다음 then이나 catch가 호출됩니다.
                    
                        const condition = true; // true면 resolve, false면 reject
                        const promise = new Promise((resolve, reject) => {
                            if (condition) {
                                resolve('성공');
                            } else {
                                reject('실패');
                            }
                        });

                        promise
                            .then((message) => {
                                return new Promise((resolve, reject) => {
                                    resolve(message);
                                })
                            })
                            .then((message2) => {
                                console.log(message2);
                                return new Promise((resolve, reject) => {
                                    resolve(message2);
                                })
                            })
                            .then((message3) => {
                                console.log(message3);
                            })
                            .catch((error) => {
                                console.error(error);
                            })
                    
                
처음 then에서 message를 resolve하면 다음 then에서 받을 수 있습니다.
여기서 다시 message2를 resolve했으므로 다음 then에서 message3를 받았습니다.

이것을 활용해서 콜백을 프로미스로 바꿀 수 있습니다.
다음은 콜백을 쓰는 패턴 중 하나입니다.
이를 프로미스로 바꿔보겠습니다.
                    
                        function findAndSaveUser(Users) {
                            Users.findOne({}, (err, user) => { // 첫 번째 콜백
                                if (err) {
                                    return console.error(err);
                                }
                                user.name = 'zero';
                                user.save((err) => { // 두 번째 콜백
                                    if (err) {
                                        return console.error(err);
                                    }
                                    Users.findOne({ gender: 'm' }, (err, user) => {
                                        // 생략
                                    })
                                })
                            })
                        }
                    
                
콜백 함수가 세 번 중첩되어 있습니다.
콜백 함수가 나올 때마다 코드의 깊이가 깊어집니다.
각 콜백 함수마다 에러도 따로 처리해줘야 합니다.
이 코드를 다음과 같이 바꿀 수 있습니다.
                    
                        function findAndSaveUser(Users) {
                            Users.findOne({})
                                .then((user) => {
                                    user.name = 'zero';
                                    return user.save();
                                })
                                .then((user) => {
                                    return Users.findOne({ gender: 'm' })
                                })
                                .then((user) => {
                                    // 생략
                                })
                                .catch(err => {
                                    console.error(err);
                                })
                        }
                    
                
코드의 깊이가 더 이상 깊어지지 않습니다.
then 메서드들은 순차적으로 실행됩니다.
콜백에서 매번 따로 처리해야 했던 에러도 마지막 catch에서 한번에 처리할 수 있습니다.
하지만 모든 콜백 함수를 위와 같이 바꿀 수 있는 것은 아닙니다.
메서드가 프로미스 방식을 지원해야 합니다.
예제의 코드는 findOne과 save 메서드가 내부적으로 프로미스 객체를 가지고 있어서 가능한 것입니다.
지원하지 않는 경우 프로미스로 바꿀 수 있는 방법은 3.5.6절에 나와 있습니다.
                    
                        const promise1 = Promise.resolve('성공1');
                        const promise2 = Promise.resolve('성공2');
                        Promise.all([promise1, promise2])
                            .then((result) => {
                                console.log(result); //['성공1', '성공2']
                            })
                            .catch((error) => {
                                console.error(error);
                            })
                    
                
Promise.resolve는 즉시 resolve하는 프로미스를 만드는 방법입니다.
비슷한 것으로 즉시 reject하는 Promise.reject도 있습니다.
프로미스가 여러 개 있을 때 Promise.all에 넣으면 모두 resolve될 때까지 기다렸다가 then으로 넘어갑니다.
result 매개변수에 각각의 프로미스 결과값이 배열로 들어 있습니다.
Promise 중 하나라도 reject가 되면 catch로 넘어갑니다.