1. promise란?
- 비동기 작업의 최종 완료 또는 실패를 나타내는 객체.
- 함수에 콜백을 전달하는 대신에, 콜백을 첨부하는 방식의 객체.
function successCallback(result) {
console.log("Audio file ready at URL: " + result);
}
function failureCallback(error) {
console.log("Error generating audio file: " + error);
}
createAudioFileAsync(audioSettings, successCallback, failureCallback);
- 위와 같이 음성파일을 생성해주는 createAudioFileAsync()라는 함수가 있고, successCallback, failureCallback의 2개의 콜백함수를 받는 함수가 있다.
- 위의 코드를 promise로 반환해주도록 수정해보면
createAudioFileAsync(audioSettings).then(successCallback, failureCallback);
const promise = createAudioFileAsync(audioSettings);
promise.then(successCallback, failureCallback);
2.promise의 특징
- call back은 자바스크립트 이벤트 루프가 현재 실행중인 call stack을 완료하기 이전에는 절대 호출되지 않음
- 비동기 작업이 성공하거나 실패한뒤에 then()을 이요하여 추가한 콜백의 경우에도 위와 같음.
- then()을 여러버 사용하여 여러개의 콜백 추가 할 수 있음
그리고 각각의 콜백은 주어진 순서대로 하나하나 실행
3. promise의 상태
- 대기(pending): 이행되거나 거부되지 않는 초기상태
- 이행(fulfilled): 연산이 성공적으로 완료됨
- 거부(rejected): 연산이 실패함
4. 용어
- resolve: 주어진 값으로 이행
- reject: 주어진 이유로 거부
- then: 이행값 기록
- catch: 거부 이유 기록
5.chaining
- 순차적으로 각각의 작업이 이전 단계 비동기 작업이 성공하나서 그 결과값을 이용하여 다음 비동기 작업을 실행해야 하는 경우에
promise chain을 이용함.
- then() 새로운 promise를 반환하고, 처음에 만들었던 promise와는 다른 새로운 promise.
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
const promise2 = doSomething().then(successCallback, failureCallback);
- promise2는 domSomething()뿐만 아니라 성공할때와 실패할때의 콜백함수의 완료를 의미함.
- 2개의 콜백함수 또한 promise를 반환하는 비동기 함수일수도 있음.
- 이 경우엔 Promise2에 추가된 콜백은 successCallback 또는 failureCallback에 의해 반환된 promise 뒤에 대기함.
- 기본적으로 각각의 promise는 체인 안에서 서로 다른 비동기 단계의 완료를 나타냄
- 예전에 콜백함수로만 이루어진 코드
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
- then()에 넘겨지는 인자는 선택적임
- 그리고 catch(failureCallback)은 then(null, failureCallback)의 축약.
- 위의 코드를 화살표함수로 나타내면,
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
6. Chaining after a catch
- chain에서 작업이 실패한 후에도 새로운 작업을 수행하는 것이 가능함
new Promise((resolve, reject) => {
console.log('Initial');
resolve();
})
.then(() => {
throw new Error('Something failed');
console.log('Do this');
})
.catch(() => {
console.log('Do that');
})
.then(() => {
console.log('Do this, whatever happened before');
});
- 위의 코드를 실행하게 되면, 다음과 같이 텍스트가 콘솔에 나옴
Initial
Do that
Do this, whatever happened before
- 첫번째 then() new Error()가 reject를 호출, catch()가 실행됨.
7. Error propagation
- 콜백함수의 경우 콜백마다 에러함수를 호출했는데, promise chain 한 번만 호출함.
- 기본적으로 promise chain은 예외가 발생하면 멈추고 chain의 아래에서 catch를 찾습니다. 이것은 동기코드가 어떻게 동작하는지 모델링 한 것.
try {
const result = syncDoSomething();
const newResult = syncDoSomethingElse(result);
const finalResult = syncDoThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
- 위의 코드를 async/await구문으로 바꾼다면,
async function foo() {
try {
const result = await doSomething();
const newResult = await doSomethingElse(result);
const finalResult = await doThirdThing(newResult);
console.log(`Got the final result: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
- async와 await는 promise를 기반으로 함.
- promise는 모든 오류를 잡아내어, 예외 및 프로그래밍 오류가 발생해도 콜백지옥의 근본적인 결함을 해결함. 이는 비동기 작업의 기능 구성에 필수적.
8.promise rejection events
- promise가 reject될 때마다 2가지 이벤트 중하나가 전역 범위에 발생(전역범위는 window이거나 웹워커에서 사용되는 경우, worker 혹은 worker 기반 인터페이스)
- 전역범위에 발생되는 이벤트
- 위의 이벤트들은 멤버변수인 promise와 reason 속성이 있음.
- promise: reject된 promise를 가리키는 속성
- reason: promiserk reject된 이유를 알려주는 속성
- 이들을 이용해 프로미스에 대한 에러처리를 대체하는 것이 가능해짐
- 또한 프로미스 관리시 발생하는 이슈들을 디버깅하는데 도움을 얻을 수 잇음.
- 이 핸들러들은 전역적이기 때문에 모든 에러는 발생한 지점에 상관없이 동일한 핸들러로 전달됨.
- unhandlerejection 이벤트 핸들러를 사용한 예
window.addEventListener("unhandledrejection", event => {
event.preventDefault();
}, false);
9.오래된 콜백api를 사용하여 promise 만들기
- promise는 생성자를 사용하여 처음부터 생성가능
- 이것은 오래된 api를 감쌀 때만 필요함
- setTimeout()를 예를 들어
setTimeout(() => saySomething("10 seconds passed"), 10000);
- 예전 스타일의 콜백과 promise를 합치는 것은 문제가 있음
- 함수가 실패하거나 프로그래밍 오류가 있으면 아무것도 잡아내지 않기 때문에.
- 하지만 promise로 setTimeout() 래핑 가능
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
참고