: 요청에 대한 결과가 동시에 일어난다. 특정 코드의 실행이 완료될 때까지 기다리고 난 후 다음 코드를 수행
( 작업은 순차적으로 실행되며 어떤 작업이 수행 중이면 다음 작업은 대기한다. )
: 요청에 대한 결과가 동시에 일어나지 않는다. 특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드들을 수행
( 작업이 종료되지 않은 상태라도 대기하지 않고 다음 작업을 수행한다. )
setTimeout(callback, millisecond)
: 일정 시간 후에 함수를 실행
clearTimeout(timerId)
: setTimeout
타이머 종료
setInterval(callback, millisecond)
: 일정 시간의 간격을 가지고 함수를 반복적으로 실행
clearInterval(timerId)
: setInterval
타이머를 종료
- blocking : 다른 함수를 호출할 때 제어권도 함께 넘겨주고, 작업이 끝난 후에 돌려받는 방식
- non-blocking : 다른 함수를 호출할 때 제어권을 넘기지 않고, 자신이 갖고 작업하는 방식
: 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
(함수를 명시적으로 호출하는 방식이 아닌 특정 이벤트가 발생했을 때 시스템에 의해 호출된다.)
: 콜백 함수 호출이 중첩되어 복잡도가 높아지는 현상
function doStep1(init, callback) {
const result = init + 1;
callback(result);
}
function doStep2(init, callback) {
const result = init + 2;
callback(result);
}
function doStep3(init, callback) {
const result = init + 3;
callback(result);
}
// 콜백 지옥
function doOperation() {
doStep1(0, result1 => {
doStep2(result1, result2 => {
doStep3(result2, result3 => {
console.log(`result: ${result3}`);
// 이를 반복하다보면 코드가 피라미드 형태를 띈다.
});
});
});
}
doOperation();
=> 이벤트 처리나 서버 통신과 같은 비동기적인 작업을 수행하기 위해 자주 등장하는데,
이러한 방식은 코드의 가독성을 나쁘게 하며, 코드 수정이 어려워진다.
Promise
사용 비동기 처리를 수행할 콜백 함수(executor)를 인수로 전달받는데, 이 콜백 함수는 resolve
와 reject
함수를 인수로 전달받는다. ( 비동기 처리 상태와 처리 결과를 관리하는 객체이다. )
Promise 객체가 생성되면 executor는 자동으로 실행되고 작성했던 코드들이 작동된다.
=> 코드가 정상적으로 실행되면 resolve 함수 호출 / 에러 발생했을 경우 reject 함수 호출
Promise는 class 이기 때문에 new 키워드를 통해 Promise 객체를 생성한다.
// Promise 생성
const promise = new Promise((resolve, reject) => {
// Promise 함수의 콜백 함수 내부에서 비동기 처리 수행
if (/* 비동기 처리 성공 */) {
resolve('result');
} else { /* 비동기 처리 실패 */
reject('failure reason');
}
});
new Promise 가 반환하는 Promise 객체는 state
, result
내부 프로퍼티를 갖는다.
but, 직접 접근이 불가능하며 .then
, .catch
, .finally
메서드를 사용해 접근 가능하다.
- State
: 기본 상태는 pending(대기) / 비동기 처리를 수행할 콜백 함수(executor
)가 성공적으로 작동하면 fulfilled
(이행)으로 변경, 에러가 발생하면 rejected
(거부)
- Result
: 처음은 undefined
/ 비동기 처리를 수행할 콜백 함수(executor
)가 성공적으로 작동하여 resolve(value)
가 호출되면 value
, 에러가 발생하면 reject(error)
가 호출되면 error
resolve
호출reject
호출- resolve(value)
: 일이 성공적으로 끝난 경우 그 결과를 나타내는 value
와 함께 호출
- reject(error)
: 에러 발생 시 에러 객체를 나타내는 error
와 함께 호출
.then()
: 비동기 처리 성공 & 실패
promise.then(
function(result) { /* 결과(result)를 다룬다 */ },
function(error) { /* 에러(error)를 다룬다 */ }
);
( 작업이 성공적으로 처리된 경우만 다루고 싶다면 .then
에 인수를 하나만 전달한다. )
.catch()
: 예외
에러가 발생한 경우만 다루고 싶다면 첫 번째 인수로 null
을 전달하면 된다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동
promise.catch(alert); // 1초 뒤 "에러 발생" 출력
executor에 작성했던 코드들이 에러가 발생했을 경우, reject
함수를 호출하고 .catch
메서드로 접근할 수 있다.
Finally
: executor에 작성했던 코드들의 정상 처리 여부와 상관없이 .finally
메서드로 접근 가능
let promise = new Promise(function(resolve, reject) {
resolve("성공");
});
promise
.then(value => {
console.log(value);
// "성공"
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log("성공이든 실패든 작동!");
// "성공이든 실패든 작동!"
})
Promise chaining가 필요한 경우는 비동기 작업을 순차적으로 진행해야 하는 경우
Promise chaining이 가능한 이유는 .then
, .catch
, .finally
의 메서드들은 Promise를 리턴하기 때문에 .then
을 통해 연결할 수 있고, 에러가 발생할 경우 .catch
로 처리하면 된다.
let promise = new Promise(function(reolve, reject) {
resolve('성공');
...
});
promise
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.then((value) => {
console.log(value);
return '성공';
})
.catch((error) => {
console.log(error);
return '실패';
})
.finally(() => {
console.log('성공이든 실패든 작동!');
});
: 여러 개의 비동기 작업을 동시에 처리하고 싶을 때 사용 ( 인자로는 배열을 받는다. )
const promiseOne = () => new Promise((resolve, reject) => setTimeout(() => resolve('1초'), 1000));
const promiseTwo = () => new Promise((resolve, reject) => setTimeout(() => resolve('2초'), 2000));
const promiseThree = () => new Promise((resolve, reject) => setTimeout(() => resolve('3초'), 3000));
// 기존 Promise chaining
const result = [];
promiseOne()
.then(value => {
result.push(value);
return promiseTwo();
})
.then(value => {
result.push(value);
return promiseThree();
})
.then(value => {
result.push(value);
console.log(result); // ['1초', '2초', '3초']
})
// promise.all
Promise.all([promiseOne(), promiseTwo(), promiseThree()])
.then((value) => console.log(value)) // ['1초', '2초', '3초']
.catch((err) => console.log(err));
Promise.all()
의 전달인자의 형태는 프로미스 객체의 배열이다.
Promise.all()
사용 시 then
메서드의 매개변수는 배열 형태로 전달된다.
Promise.all()
은 인자로 받는 배열에 있는 Promise
중 하나라도 에러가 발생하면 나머지 Promise
의 state와 상관없이 즉시 종료된다.
function 앞에 async
를 붙이면 해당 함수는 promise를 반환한다.
// `async`는 function 앞에 위치한다.
async function f() {
return 1;
}
f().then(alert); // 1
async
함수 내에서만 await
키워드 사용
await
를 만나면 promise가 처리될 때까지 함수 실행을 기다리고, 결과는 그 이후에 반환한다.
promise가 처리되는 것을 기다리는 동안 다른 일을 할 수 있어 CPU 리소스가 낭비되지 않는다.
// 함수 선언식
async function funcDeclarations() {
await 작성하고자 하는 코드
...
}
// 함수 표현식
const funcExpression = async function () {
await 작성하고자 하는 코드
...
}
// 화살표 함수
const ArrowFunc = async () => {
await 작성하고자 하는 코드
...
}
// `await`는 `async` 함수 안에서만 동작한다.
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료"), 1000)
});
// promise가 이행될 때까지 기다린다
let result = await promise;
alert(result); // 1초 뒤 "완료" 출력
}
f();