사용자가 화면에서 서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모른다면?
마냥 다른 코드를 실행 안 하고 기다릴 순 없다. 만약 요청이 50개라면 그 50개가 다 수행될 때까지 웹 어플리케이션을 제대로 이용할 수 없기 때문이다. 이러한 순간에 필요한 것이 비동기 실행이다. 그 비동기 실행을 구현하기 위해 사람들은 Callback function을 사용하였고, Callback function의 여러 단점을 보완하기 위해 등장한 것이 Promise이다.
function getData(callbackFunc) {
$.get('url 주소/products/1', function(response) {
callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
});
}
getData(function(tableData) {
console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});
try {
setTimeout(() => { throw new Error('Error!'); }, 1000);
} catch (e) {
console.log('에러를 캐치하지 못한다..');
console.log(e);
}
위 코드의 실행 과정이다.
try 블록 내에서 setTimeout 함수가 실행 됨 >> 1초 후 콜백 함수 실행 >> 콜백 함수가 예외(exception)를 발생시킴 >> 하지만 예외가 catch 블록에서 캐치되지 않음
setTimeout 함수는 *비동기 함수이므로 콜백 함수가 실행될 때까지 기다리지 않고 즉시 종료되어 호출 스택에서 제거된다.
이후 tick 이벤트 발생 >> setTimeout 함수의 콜백 함수는 태스트 큐로 이동 >> 호출 스택이 비어졌을 때 호출 스택으로 이동되어 실행
순으로 코드가 진행되는데,
이때 setTimeout 함수는 이미 호출 스택에서 제거된 상태이다. 이것은 setTimeout 함수의 콜백 함수를 호출한 것은 setTimeout 함수가 아니다라는 것을 의미한다. setTimeout 함수의 콜백 함수의 호출자(caller)가 setTimeout 함수라면 호출 스택에 setTimeout 함수가 존재해야 하기 때문이다.
예외(exception)는 호출자(caller) 방향으로 전파된다. 하지만 setTimeout 함수의 콜백 함수를 호출한 것은 setTimeout 함수가 아니기 때문에 setTimeout 함수의 콜백 함수 내에서 발생시킨 에러는 catch 블록에서 캐치되지 않고, 프로세스는 종료된다.
비동기 처리 함수의 콜백 함수는 해당 이벤트(timer 함수의 tick 이벤트, XMLHttpRequest의 readystatechange 이벤트 등)가 발생하면 태스트 큐(Task queue)로 이동한 후 호출 스택이 비어졌을 때, 호출 스택으로 이동되어 실행된다.
function getData(callback) {
// new Promise() 추가
return new Promise(function(resolve, reject) {
$.get('url 주소/products/1', function(response) {
// 데이터를 받으면 resolve() 호출
resolve(response);
});
});
}
// getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
// resolve()의 결과 값이 여기로 전달됨
console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});
Promise는 new Promise()로 생성되고 종료할 때까지 3가지 상태를 갖는다.
new Promise() 메서드를 호출하면 대기(Pending) 상태가 된다.
new Promise();
new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject이다.
```js
new Promise(function(resolve, reject) {
// ...
});
여기서 콜백 함수의 인자 resolve를 아래와 같이 실행하면 이행(Fulfilled) 상태가 된다.
new Promise(function(resolve, reject) {
resolve();
});
이행 상태가 되면 then()을 이용하여 처리 결과 값을 받을 수 있다.
function getData() {
return new Promise(function(resolve, reject) {
var data = 100;
resolve(data);
});
}
// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
console.log(resolvedData); // 100
});
new Promise()로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다. 여기서 reject를 아래와 같이 호출하면 실패(Rejected) 상태가 된다.
new Promise(function(resolve, reject) {
reject();
});
그리고, 실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()로 받을 수 있다.
function getData() {
return new Promise(function(resolve, reject) {
reject(new Error("Request is failed"));
});
}
// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
console.log(err); // Error: Request is failed
});
promiseAjax('GET', 'http://jsonplaceholder.typicode.com/posts/1')
.then(JSON.parse)
.then(render)
.catch(console.error);
참고 자료
[JavaScript] 바보들을 위한 Promise 강의 - 도대체 Promise는 어떻게 쓰는거야?
자바스크립트 Promise 쉽게 이해하기
Promise | PoiemaWeb
자바스크립트 비동기 처리와 콜백 함수