자바스크립트의 비동기 처리
는 특정 코드의 연산이 끝날 때까지 기다려주지 않고 나머지 코드를 먼저 실행하는 것을 의미한다.
JavaScript is synchronous.
Execute the code block by orger after hoisting.
아래 코드를 콘솔에서 확인하면 동기, 비동기 이해될 듯..
console.log('1')
setTimeout(() => console.log('2'), 1000);
console.log('3');
// Synchronous(동기식) callback
function printImmediately(print) {
print();
}
printImmediately(() => console.log('hello'));
// Asynchronous(비동기식) callback
function printWithDelay(print, timeout) {
setTimeout(print, timeout);
}
printWithDelay(() => console.log('async callback'), 2000);
setTimeOut()
은 Web API의 한 종류이다.
코드를 바로 실행하지 않고 지정한 시간만큼 기다렸다가 로직을 실행한다.
화면에서 서버로 데이터를 요청했을 때, 서버가 언제 그 요청에 대한 응답을 줄 지 모르는데 마냥 다른 코드를 실행하지 않고 기다릴 수 없기 때문 ! 너무 오래 걸려..
JavaScript: What the heck is a Callback?
콜백은 다른 함수가 실행을 끝낸 뒤 실행되는 — call back 되는 함수를 말한다.
자바스크립트에서 함수
는 object이다.
이 때문에 함수는 다른 함수의 인자로 쓰일 수도, 어떤 함수에 의해 리턴될 수도 있다. 이러한 함수를 고차 함수 higher-order functions 라 부르고 인자로 넘겨지는 함수를 콜백 함수 callback function 라고 부른다.
콜백 지옥
은 비동기 처리 로직을 위해 콜백 함수를 연속해서 사용할 때 발생하는 문제이다.
"A promise is an object that may produce a single value some time in the future"
프로미스
는 자바스크립트 비동기 처리 에 사용되는 객체다.
주로 서버에서 받아온 데이터를 화면에 표시할 때 사용한다.
프로미스 객체는 아래와 같은 문법으로 만들 수 있다.
Producer
const promise = new Promise((resolve, reject) => {
// executor
console.log('doing something...');
setTimeout(() => {
resolve('yezo..'); // 성공하면
reject(new Error('no network')); // 실패하면
}, 2000);
});
Consumer
promise
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => { // 성공하든 실패하든 마지막에 실행됨.
console.log('finally');
})
State : pending(보류) -> fulfilled(이행) or rejected(거부)
여러 개의 프로미스를 연결하여 사용할 수 있다.
then()
메서드를 호출하고 나면 새로운 프로미스 객체가 반환된다.
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 2000);
})
.then((result) => {
console.log(result);
return result + 10; // 1
})
.then((result) => {
console.log(result);
return result + 20; // 11
})
.then((result) => {
console.log(result); // 31
});
프로미스 객체 하나를 생성하고 setTimeout()
을 이용해 2초 후에 resolve()
를 호출하는 코드가 있다.
resolve()
가 호출되면 프로미스가 Pending 상태에서 Fulfilled 상태로 넘어가기 때문에 첫 번째 then()
의 로직으로 넘어간다.
첫 번째 then()
에서는 이행된 결과 값 1
을 받아서 10
을 더한 후, 그 다음 then()
으로 넘겨준다.
두 번째 then()
에서도 바로 이전 프로미스의 결과 값 11
을 받아서 20
을 더하고 다음 then()
으로 넘겨준다.
마지막 then()
에서 최종 결과 값 31
을 출력한다.
then()
의 두 번째 인자로 에러를 처리하기catch()
를 이용하기function getData() {
return new Promise((resolve, reject) => {
reject('failed');
});
}
// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData()
.then(() => {
// ...
}, (err) => {
console.log(err);
});
// 2. catch()로 에러를 처리하는 코드
getData()
.then()
.catch((err) => console.log(err));
프로미스 에러 처리는 가급적 catch()를 사용하자 !
더 효율적이다..
더 많은 예외 처리를 위해 catch()를 붙여주자..
async & await = clear style of using Promise.
async
와 await
는 자바스크립트 비동기 처리 패턴 중에 가장 최근에 나온 문법이다.
기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 좀 더 읽기 좋은 코드를 작성할 수 있게 도와준다.
프로미스 체이닝을 계속 하다보면 코드의 가독성이 떨어지게 된다.
async
와 await
는 Promise를 간결하고 동기적으로 실행되는 것처럼 보이게 만들어준다.
async
와 await
는 새로운 것이 추가된 것이 아니라, 기존에 존재하는 Promise 위에 조금 더 간편한 API를 제공한다. -> syntatic sugar
Promise 사용 방식
function fetchUser() {
return new Promise((resolve, reject) => {
return 'yezo'; // 프로미스 state : pending
resolve('yezo'); // 프로미스 state : fulfilled
reject(new Error('error')); // 프로미스 state : rejected
});
}
const user = fetchUser();
user.then(user => console.log(user));
Async 사용
// 1. 함수 선언식
async function fetchUser() {
return 'yezo..';
}
// 2. 함수 표현식
const fetchUser = async function() {
return 'yezo';
}
// 3. 화살표 함수
const fetchUser = async() => {
return 'yezo';
}
const user = fetchUser(); // 변수에 할당해서 호출
//fetchUser().then(data => console.log(data)); // 함수로 바로 호출
user.then(console.log);
Promise 방식
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function getApple() {
return delay(2000)
.then(() => '🍎');
}
function getBanana() {
return delay(1000)
.then(() => '🍌');
}
function pickFruits() {
return getApple()
.then(apple => {
return getBanana()
.then(banana => {
`${apple} + ${banana}`
});
}
}
pickFruits().then(result => console.log(result));
Await 사용
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(2000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
async function pickFruits() {
let apple = null;
try {
apple = await getApple();
} catch(error) {
console.log(error);
}
let banana = null;
try {
banana = await getBanana();
} catch(error) {
console.log(error);
}
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
Await 병렬 처리
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(2000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
// 1. 무식한 코드
// 프로미스 객체는 선언 즉시 바로 실행됨.
// getApple과 getBanana는 병렬(독립적)으로 실행됨.
async function pickFruits() {
const applePromise = getApple();
const bananaPromise = getBanana();
// 동기화
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
// 2. useful Promise APIs ✨
function pickAllFruits() {
return Promise.all([getApple(), getBanana()])
.then(fruits => {
fruits.join(' + ');
});
}
pickAllFruits().then(console.log);
// 비동기 처리 중 먼저 리턴하는 것만 출력
function pickOnlyOne() {
return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);