Skip to content

Latest commit

 

History

History
934 lines (687 loc) · 24.2 KB

ch11.Promise.Async.Taek.md

File metadata and controls

934 lines (687 loc) · 24.2 KB

ch11 프로미스와 비동기 프로그래밍

11.1 비동기 프로그래밍 배경 지식

  • 자바스크립트 엔진은
    • 싱글 스레드
      • 이벤트 루프 개념을 기반
    • 한 번에 한 개의 코드만 실행된다는 의미
    • 작업 큐
      • 실행 예정인 코드가 저장되는 곳
    • 이벤트 루프
      • 자바스크립트 엔진 내의 프로세스
      • 코드 실행감시
      • 작업 큐 관리

11.1.1 이벤트 모델

let button = document.getElementById("my-btn");
button.onclick = function(event) {
    console.log("Clicked");
}
  • 이벤트는 사용자 인터랙션에 대한 응답이나 그와 유사한 기능에 유용
  • 더 복잡한 요구사항에는 유연하지 않다

11.1.2 콜백 패턴

  • 호출할 함수가 인자로 전달되는 차이
  • 콜백 패턴은 비동기 코드가 특정 시점까지 실행되지 않는다는 점에서 이벤트 모델과 유사
readFile("example.txt", function(err, contents) {
   if (err)
       throw err;
    console.log(contents);
});
console.log("Hi!");
  • Node.js: error first callback style을 사용

    • readFile()은 즉시 실행되기 시작하고 디스크로부터 파일을 읽기 시작할 때 멈춘다.
      • console.log("Hi!");가 먼저 실행된다.
  • readFile()이 실행 완료

    • 작업 큐의 맨 뒤에
      • 콜백 함수와
      • 콜백 함수의 인자를 가진 새로운 작업이
        • 추가
          • 앞서 모든 작업 완료 후 실행
// 콜백 패턴은 여러 개의 호출 연결이 쉬워
// 이벤트보다 더 유연하다

readFile("example.txt", function(err, contents) {
   if (err)
       throw err;
    
    writeFile("example.txt", function(err) {
       if (err)
           throw err;
        
       console.log("File was written!");
    });
});
  • 길어지면 HELL!!!!!!!!!!!!!!!!!!

11.2 프로미스 기초

  • 프로미스는 비동기 연산의 결과를 위한 플레이스 홀더
  • 이벤트를 구독하거나 함수에 콜백을 전달하는 대신
    • 프로미스를 반환할 수 있다.
// readFile이 앞으로 어떤 시점에 완료할 것을 약속함
let promise = readFile("example.txt");
  • 이 코드에서 readFile()은 파일을 즉시 읽기 시작하지 않고
    • 나중에 읽는다.
      • 대신
        • 함수는 비동기 읽기 연산을 나타내는 프로미스 객체를 반환
          • 나중에 그 작업을 실행가능
            • 언제 작업할 수 있는지는 전적으로 프로미스의 생명주기가 어떻게 종료되는지에 달려 있다.

11.2.1 프로미스 생명주기

  • pending: 비동기 연산이 아직 완료되지 않았음을 가리키는 보류

    • 그리고 짧은 생명주기를 통과
      • 보류 프로미스는
        • 미확정(unsettled) 상태로 간주된다.
  • 위 예제의 프로미스는 readFile() 함수가

    • 그 프로미스를 반환하는 시점에 보류 상태에 있게 됨다.
  • 비동기 연산이 완료

    • 이 프로미스는
      • 확정(settled) 상태로 간주
        • 두 가지 가능한 상태 중 하나가 된다.
  • 성공(Fulfilled):

    • 프로미스의 비동기 연산이 성공적으로 완료되었다.
  • 실패(Rejected):

    • 프로미스의 비동기 연산이 에러나 다른 이유 때문에 성공적으로 완료되지 않았다.
  • 프로미스 내부의 [[PromiseState]] 프로퍼티는 프로미스의 상태를 반영하여

    • pending
    • fulfiled
    • rejected
  • 로 설정

    • 이 프로퍼티는 프로미스 객체에 노출되지 않으므로 프로그래적으로 어떤 상태를 정할 수 없다.
  • BUT~~~~~~~~~~

    • then() 메서드를 사용하여 프로미스의 상태가 변경될 때 특정 동작을 취하도록 할 수 있다.
  • then() 메서드는 모든 프로미스에 존재하며 두 개의 인자를 받는다.

    • 첫 번째:
      • 성공했을 때 호출하는 함수
        • 추가적인 데이터는 성공함수
    • 두 번째:
      • 프로미스가 실패했을 때 호출할 함수
        • 추가적인 데이터는 실패함수
let promise = readFile("example.txt");

promise.then(
    function (contents) {
   	// 성공 시
    console.log(contents);
	}, 
    function(err) {
    // 실패 시
    console.log(err.message);
});

promise.then(function(contents) {
   	// 성공 시
    console.log(contents);    
});

promise.then(null, function(err) {
    // 실패 시
    console.log(err.message);
});

promise.catch(function(err) {
    // 실패 시
    console.log(err.message);    
});

세 가지 then()은 모두 같은 프로미스에서 호출되고 있다.

  • 성공, 실패
  • 성공
  • , 실패..... then(null, function~~
  • 실패만.... catch(function~~

만 처리 하는 중

  • 장점

    • 이벤트는 에러 존재 시 발생 x
    • 콜백은 항상 에러 인자 검사를 수행해야만 한다
  • 프로미스가 이미 확정 상태가 된 후 작업큐에 추가되었더라도 성공이나 실패 핸들러는 여전히 실행될 것이다.

    • 이는 언제든지 새로운 성공핸들러나 실패 핸들러를 추가할 수 있으며 그에 대한 호출을 보장한다는 의미
let promise = readFile("example.txt");

// 원본 성공 핸들러
promise.then(function(contents) {
    console.log(contents);
    
    // 또 다른 성공 핸들러 추가
    promise.then(function(contents) {
       console.log(contents); 
    });
});

11.2.2 미확정 프로미스 만들기

  • 새로운 프로미스는
    • new Promise()
      • 이 때 생성자는 프로미스 초기화 코드를 포함하는
        • 실행자(executor) 함수 하나를 인자로 받는다
          • 실행자에는
            • resolve()
            • reject()
          • 두 함수가 인자로 전달
// Node.js 예제

let fs = require("fs");

function readFile(filename) {
    return new Promise((resolve, reject) => {
        // 비동기 연산 수행
        fs.readFile(
            filename, 
            { encoding: "utf8"}, 
            function(err, contents) {
				// failed
                if(err) {
                    reject(err);
                    return;
                }
                
                // succeeded
                resolve(contents);
            }
        );
    });
}

let promise = readFile("example.txt");

promise.then(
    function(contents) {
        // 성공 시
        console.log(contents);
    },
    function(err) {
        // 실패 시
        console.err(err.message);
    }
);
  • resolve() 호출은
    • 비동기 연산을 작동 시킨다.
  • 프로미스를 생성하면
    • 프로미스의 실행자가 실행을 완료 후
      • 성공과 실패 핸들러가 항상 작업큐의 맨 뒤에 추가된다.. .lol

11.2.3 확정 프로미스 만들기

  • Promise 생성자는 미확정 프로미스를 만들기 위한 최적의 방법이며,
    • 이는 프로미스 실행자가 실행을 동적으로 하는 특징 때문이다.
      • 그러나 프로미스가 한 가지 값만 표현하길 원한다면,
        • 단순히 resolve() 함수에 값을 전달하는 작업을 스케줄링 하는 것은 의미가 없다.
let promise = Promise.resolve(42);

promise.then(function(value) {
    console.log(value);	// 42
}); // 실패 핸들러 절대 호출x

promise = Promise.reject(42);
promise.catch(function(value) {
   console.log(value); // 42 
}); // 성공 핸들러 절대 호출x
  • promise.resolve()나 Promise.reject() 메서드 중 하나에 프로미스를 전달하면,
    • 그 프로미스는 변경없이 그대로 반환된다.
프로미스가 아닌 대너블
  • Promise.resolve(), Promise.reject() 둘 다 인자로 프로미스가 아닌 대너블(thenable)도 받을 수 있다.
    • 프로미스가 아닌 대너블이 전달되면 이 메서드는 then() 함수 이후에 호출되는 새로운 프로미스를 만든다.
let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
}
  • 이 예제에서 thenable 객체는 then() 메서드 외에 프로미스와 관련된 특징이 없다.
    • thenable을 성공한 프로미스로 변경하기 위해 Promise.resolve()를 호출할 수 있다.
let thenable = {
    then: function(resolve, reject) {
        resolve(42);
    }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
   console.log(value); // 42 
});
  • Promise.resolve()는

    • thenable.then()을 호출
      • 프로미스 상태를 결정
  • 이와 비슷하 방식으로 thenable에서 실패한 프로미스를 만들 수 있다.

  • why??????/

    • es6 이전 라이브러리들은
      • 프로미스 도입 전 부터
        • thenable 사용
          • 그래서 호환성을 갖기 위해 thenable 추가
  • 그래서

    • 객체가 프로미스인지 잘 모르면
      • Promise.resolve(), Promise.reject()에 객체를 전달하는 것이 프로미스를 알아내는 좋은 방법
        • 프로미스일 경우 변경없이 전달될 것이기 때문이다.

11.2.4 실행자 에러

  • 만약 실행자에서 에러가 발생하면 프로미스의 실패 핸들러가 호출된다.
let promise = new promise(function(resolve, reject) {
   throw new Error("Burst!!!!!"); 
});

promise.catch(function(error) {
    console.log(error.message); // "Explosion!"
});
  • 모든 실행자는 내부적으로 try-catch를 가지고 있으므로 에러를 잡고 에러 객체를 실패 핸들러에 전달

  • 위 코드는 아래와 같은 코드

let promise = new promise(function(resolve, reject) {
    try {
	   throw new Error("Burst!!!!!");         
    } catch (ex) {
    	reject(ex);
    }
});

promise.catch(function(error) {
    console.log(error.message); // "Explosion!"
});

11.3 전역 프로미스 실패 처리

  • 프로미스에서
    • 가장 논란이 되는 부분은???
      • 실패 핸들러 없이 실패했을 때 발생하는 암묵적인 실패!!!
    • 프로미스 특성 상
      • 프로미스 실패가 처리되었는지 판단하는 것은 프로미스의 특성 때문에 간단하지 않다.
let rejected = Promise.rejected(42);
// 이 시점에서는 실패 처리x

// 어느 정도 시간이 흐른 후...
rejected.catch(function(value) {
   // 이제 실패가 처리됨
    console.log(value);
});

11.3.1 Node.js 실패 처리

  • Node.js는 프로미스 실패 처리와 관련된
    • process 객체에서 두 개의 이벤트를 발생 시킨다.
      • unhandledRejection:
        • 프로미스가 실패하고 같은 이벤트 루프 턴에서 실패 핸들러가 호출되지 않으면 발생
          • 인자:
            • 실패 이유 (에러 객체)
            • 실패한 프로미스
      • rejectionHandled:
        • 프로미스가 실패하고 이벤트 루프의 턴 이후 실패 핸들러가 호출되면 발생
          • 인자:
            • 실패한 프로미스
let rejected;

process.on("unhandledRejection", function(reason, promise) {
	console.log(reason.message); // "Burst!" 
    console.log(rejected == promise); // true
});

rejected = Promise.reject(new Error("Burst!"));
let rejected;
process.on("rejectionHandled", function(promise) {
   console.log(rejected === promise); // true 
});

rejected = Promise.reject(new Error("Burst!"));

// 실패 핸들러 추가를 기다림
setTimeout(function(){
    rejected.catch(function(value) {
        console.log(value.message); // "Burst!"
    });
}, 1000);
  • rejectionHandled 이벤트는 실패 핸들러가 최종적으로 호출되었을 때 발생한다.

    • 만약 실패 핸들러가 rejected가 만들어진 후 rejected에 직접 연결 되었다면, 이 이벤트는 발생하지 않았을 것이다. ???
      • 즉, 실패 핸들러가 rejected가 만들어진 이벤트 루프와 같은 턴에서 호출되는 경우, 유용하지 않다.
  • 처리되지 않을 가능성이 있는 실패를 적절히 추적하려면, rejectionHandled와 unhandledRejection 이벤트를 사용해 그 실패들의 리스트를 저장

    • 그리고 나서 리스트를 검사하기 위해서 약간의 시간을 기다려야 한다.???
let possiblyUnhandledRejections = new Map();

// 실패가 처리되지 않았을 때, Map에 실패를 추가
process.on("unhandledRejection", function(reason, promise) {
   possiblyUnhandledRejections.set(promise, reason); 
});

process.on("rejectionHandled", function(promise) {
    possiblyUnhandledRejections.delete(promise);
});

setInterval(function() {
    possiblyUnhandledRejections.forEach(function(reason, promise) {
        console.log(reason.message ? reason.message : reason);
        
        //이 실패를 처리하기 위한 작업 수행
        handleRejection(promise, reason);
    });
    
    possiblyUnhandledRejections.clear();
    
}, 60000);
  • Map
    • key: promise
    • value: 프로미스의 이유
  • 과정
    • unhandledRejection 이벤트가 발생할 때마다 프로미스와 실패 이유는 Map에 추가된다.
    • rejectionHandled 이벤트가 발생할 때마다 Map에서 처리된 프로미스가 제거된다.
      • possiblyUnhandledRejections는 이벤트가 호출될 때마다 늘어나고 줄어든다.
    • setInterval() 호출은 처리되지 않을 가능성이 있는 실패 리스트를 주기적으로 검사하고 콘솔에 정보를 출력한다.
      • 실제로는 로그를 남기거나 다른 방법으로 처리
  • 위 예제에서는 어떤 프로미스가 존재하는지 살펴보기 위해 정기적으로 검사할 필요가 있기 때문에
    • Weak Map 대신 Map 사용?????????????????????????????

11.3.2 브라우저 실패 처리

  • 브라우저도 마찬가지로 처리되지 않은 실패를 식별하도록 돕는 두 개의 이벤트를 발생시킨다.
    • 이 이벤트들은 window 객체에서 발생하며 Node.js와 실질적으로 동일하다.
  • 종류
    • unhandledrejection: 프로미스가 실패하고 같은 이벤트 루프 턴에서 실패 핸들러가 호출되지 않으면 발생
    • rejectionhandled: 프로미스가 실패하고 이벤트 루프의 턴 이후 실패 핸들러가 호출되면 발생

! Node.js 구현에서 이벤트 핸들러는 개별적인 매개변수를 전달받지만, 브라우저의 이벤트 핸들러는 다음 프로퍼티를 가진 이벤트 객체를 받는다.

  • 이벤트 객체
    • type: 이벤트의 이름("unhandledrejection"이나 "rejectionhandled")
    • promise: 실패한 프로미스 객체
    • reason: 프로미스로부터 받은 실패 이유
  • 브라우저 구현의 차이점은 실패 이유(reason)를 두 이벤트에서 모두 이용할 수 있다는 것이다.
let rejected;
window.onunhandledrejection = function(event) {
	console.log(event.type); // "unhandledrejection"
    console.log(event.reason.message); // "Burst!"
    console.log(rejected === event.promise) // true
};

window.onrejectionhandled = function(event) {
    console.log(event.type); // "rejectionhandled"
    console.log(event.reason.message); // "Burst!"
    console.log(rejected === event.promise); // true
};

rejected = Promise.reject(new Error("Burst!"));
  • 위 처럼 DOM Level 0 표기법인 onunhandledrejection과 onrejectionhandled를 사용하여 두 이벤트 핸들러를 할당

  • or

  • addEventListener("unhandledrejection")과 addEventListener("rejectionhandled") 사용 가능

  • 각 이벤트 핸들러는 실패한 프로미스에 대한 정보를 가진 이벤트 객체를 받는다.

    • type
    • promise
    • reason
  • 브라우저에서 처리되지 않은 실패를 추적하는 코드는 Node.js와 매우 유사

let possiblyUnhnadledrejections = new Map();

// 실패가 처리되지 않았을 때, Map에 실패를 추가
window.onunhandledrejection = function(event) {
	possiblyUnhandledRejections.set(event.promise, event.reason);  
};

window.onrejectionhandled = function(event) {
  possiblyUnhandledRejections.delete(event.promise);  
};

setInterval(function() {
    possiblyUnhandeldRejections.forEach(function(reason, promise) {
		console.log(reason.message ? reason.message : reason);
        
        // 위 실패를 처리하기 위한 작업 수행
        handleRejection(promise, reason);
    });
    
    possiblyUnhandledRejections.clear();
    
}, 60000);
  • 이벤트 핸들러에서 정보를 저장받는 위치

11.4 프로미스 연결하기

  • 프로미스
    • 콜백과 setTimeout() 함수의 조합을 약간 개선??
      • 보기보다 더 많은 장점이 있다.
  • 특히, 복잡한 비동기 동작을 수행하기 위해 프로미스를 연결하는 여러 가지 방법이 존재한다.
    • then()
    • catch()
      • 또 다른 프로미스를 만들어 반환한다.
      • 이 두번째 프로미스는 첫 번째 프로미스가 성공하거나 실패했을 때만 처리된다.
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

p1
    .then(function(value) {
    	console.log(value); 
	}).then(function() {
    	console.log("finished!");
	});
42
finished

###11.4.1 에러 처리

let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

p1.then(function(value) {
	throw new Error("Boom!");        
}).catch(function(error) {
    console.log(error.message); // "Boom!"
});


p1.then(function(value) {
	throw new Error("Boom!");        
}).then(null, function(error) {
    console.log(error.message); // "Boom!"
});

p1.then(function(value) {
	throw new Error("Boom!");        
}).then((value) => {
    console.log(value);
}, function(error) {
    console.log(error.message); // "Boom!"
}).then(function(value) {
    console.log("last");
});

p1.then(function(value) {
	throw new Error("Boom!");        
}).catch(function(err) {
   	console.log("first error: " + err);
    return 42;
//    return new Promise(resolve, reject) {
//        console.log("inside first error:!");
//        resolve(0);
//    };
}).then((value) => {
    console.log(value + " after error!");
}, function(error) {
    console.log(error.message); // "Boom!"
}).then(function(value) {
    console.log("last");
}).catch(function(err) {
    console.log("last error: " + err);
});
  • 프로미스 연결 마지막에 실패 핸들러를 추가하면 어떤 발생가능한 에러든 적절하게 처리되는 것을 항상 보장

11.4.2 프로미스 연결에서 값 반환하기

  • 다음 프로미스에 데이터를 전달하는 기능
// 성공 핸들러
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

p1.then(function(value) {
	console.log(value); // 42
    return value + 1;
}).then(function(value) {
    console.log(value) // 43
});

// 실패 핸들러
p1 = new Promise(function(resolve, reject) {
   reject(42); 
});

p1.catch(function(value) {
	console.log(value); // 42
    return value + 1;
}).then(function(value) {
    console.log(value) // 43
});

11.4.3 프로미스 연결에서 프로미스 반환하기

let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p2 = new Promise(function(resolve, reject) {
   resolve(43); 
});

p1.then(function(value) {
	// 첫 번째 성공 핸들러
    console.log(value); // 42
    return p2;
}).then(function(value) {
	// 두 번째 성공 핸들러
    console.log(value); // 43 
});
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p2 = new Promise(function(resolve, reject) {
   resolve(43); 
});

let p3 = p1.then(function(value) {
	// 첫 번째 성공 핸들러
    console.log(); // 42
    return p2;
});

p3.then(function(value) {
   	// 두 번째 성공 핸들러
    console.log(value);
});
  • *** 두 번째 성공 핸들러가 p2가 아닌 p3 즉 세번째 프로미스에 추가**
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p2 = new Promise(function(resolve, reject) {
   reject(43); 
});

p1.then(function(value) {
  	// 첫 번째 성공 핸들러
    console.log(value); // 42
    return p2;
}).then(function(value) {
    // 두 번째 성공 핸들러
    console.log(value); // 호출되지 않음
});

// 에러 잡으면 나옴
p1.then(function(value) {
  	// 첫 번째 성공 핸들러
    console.log(value); // 42
    return p2;
}).catch(function(value) {
    // 두 번째 성공 핸들러
    console.log(value); // 호출되지 않음
});
  • 프로미스가 성공하던 실패하던 thenable 반환~

    let thenable = {
        then: function(resolve, reject) {
            resolve(42);
        }
    };
  • thenable을 반환한다는 것은 프로미스 결과에 대한 추가 응답을 정의할 수 있다는 의미이다.

let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

p1.then(function(value) {
    console.log(value); // 42
    
    // 새로운 프로미스를 만듬
    let p2 = new Promise(function(resolve, reject) {
       resolve(43); 
    });
    
    return p2;
}).then(function(value) {
   console.log(value); // 43 
});
  • 이 패턴은 또 다른 프로미스를 실행시키기 전에 이전의 프로미스가 확정될 때까지 기다리길 원할 때 유용하다.

11.5 여러 개의 프로미스에 응답하기

  • Promise.all()
  • Promise.race()

11.5.1 Promise.all() 메서드

  • Promise.all()
    • 관리할 프로미스들의 이터러블(배열 같은) 인자 하나를 받고, 이터러블 내 모든 프로미스가 처리된 경우에만 처리된 프로미스 하나를 반환
      • 반환된 프로미스는 다음 예제처럼 이터러블 내의 모든 프로미스가 성공했을 때 성공
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p2 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p3 = new Promise(function(resolve, reject) {
   resolve(44); 
});

let p4 = Promise.all([p1, p2, p3]);

p4.then(function(value) {
   	console.log(Array.isArray(value)); // true
    console.log(value[0]); // 42
    console.log(value[1]); // 43
    console.log(value[2]); // 44    
});
  • 하나라도 실패 시!
let p1 = new Promise(function(resolve, reject) {
   resolve(42); 
});

let p2 = new Promise(function(resolve, reject) {
   reject(42); 
});

let p3 = new Promise(function(resolve, reject) {
   resolve(44); 
});

let p4 = Promise.all([p1, p2, p3]);

p4.catch(function(value) {
   	console.log(Array.isArray(value)); // false
    console.log(value); // 43

});
  • 실패 핸들러는 배열이 아닌
    • 하나의 값만을 받는다.
      • 딱! 실패한 그 프로미스의 결과 값!

11.5.2 Promise.race() 메서드

  • 여러 개의 프로미스를 관찰
    • 하지만 약간 다른 방식
      • 프로미스ㅡ의 이터러블을 받고 프로미스 하나 반환
        • 반환된 프로미스는 첫 번째 프로미스가 확정되자마자 확정
          • Promise.all() 처럼 모든 프로미스가 성공되길 기다리는 대신!!!!!!!!!!
          • Promise.race()는 배열의 어떤 프로미스라도 성공하면 바로 그에 맞는 프로미스를 반환
let p1 = Promise.resolve(42);

let p2 = new Promise(function(resolve, reject) {
   resolve(43); 
});

let p3 = new Promise(function(resolve, reject) {
   resolve(44); 
});

let p4 = Promise.race([p1, p2, p3]);

p4.then(function(value) {
   console.log(value); // 42 
});
  • 실패 시
let p1 = Promise.resolve(42);

let p2 = new Promise(function(resolve, reject) {
   reject(43); 
});

let p3 = new Promise(function(resolve, reject) {
   resolve(44); 
});

let p4 = Promise.race([p1, p2, p3]);

p4.catch(function(value) {
   console.log(value); // 43
});
  • Promise.race()가 호출되었을 때 p2가 이미 실패 상태이기 때문에 p4 또한 실패한다.
  • p1과 p3가 성공하더라도 이는 p2가 실패한 후 발생하는 일이므로 무시된다.

11.6 프로미스 상속하기

  • 다른 내장 타입처럼 파생 클래스의 기반 클래스로 프로미스를 사용할 수 있다.
    • 이는 내장 프로미스의 기능을 확장하기 위하여 프로미스의 변형을 정의할 수 있다.
      • then(), catch() 말고
        • success, failure를 사용하고 싶을 때
class MyPromise extends Promise {
    // 기본 생성자 사용
    success(resolve, reject) {
        return this.then(resolve, reject);
    }
    
    failure(reject) {
        return this.catch(reject);
    }
}

let promise = new MyPromise(function(resolve, reject) {
    resolve(42);
});

promise.success(function(value) {
    console.log(value);
}).failure(function(value) {
   	console.log(value); 
});

11.7 프로미스 기반 비동기 작업 실행