위 사진처럼 동기적인 작업은 1, 2, 3 작업순서대로 진행되는 방식이다. 비동기적인 작업은 1, 2, 3 순서와 상관 없이 1번이 처리되는 동안에도 2번이 실행될 수 있는 방식이다. 만약, 작업이 순차적으로 이루어져야 할 때 비동기 처리를 하면 그 순서가 보장되지 않는다.
❓ 비동기 처리가 필요한 이유가 뭘까?
서버로 데이터를 요청했을 때 서버가 언제 그 요청에 대한 응답을 줄지도 모르는데 마냥 다른 코드를 실행 안 하고 기다릴 순 없기 때문이다. 만약, 100개 요청을 보낸다고 가정하면 동기 처리는 코드 실행하고 기다리고, 실행하고 기다리고.. 를 반복할 것이다. 결국, 어플리케이션이 제대로 실행되지 않거나 많은 시간을 기다려야 될 것이다.
// 콜백지옥
step1(function(value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
step5(value4, function(value5) {
// value5를 사용하는 처리
});
});
});
});
});
자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 위 소스코드는 콜백함수를 극단적으로 많이 사용한 코드로 흔히 콜백지옥이라고 말한다. 이 콜백지옥으로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며, 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다. 이러한 단점을 보완하여 만든 패턴이
promise
이다.
❓ 그렇다면
promise
를 언제 사용하게 될까?
서버에 ‘데이터 하나 보내주세요’ 라는 요청을 할 경우, 데이터를 받아오기도 전에 마치 데이터를 다 받아온 것 처럼 화면에 데이터를 표시하려고 하면 오류가 발생하거나 빈 화면이 뜬다. 이와 같은 문제를 막고자 비동기 동작을 동기적으로 받아 처리할 때 사용한다.
function sayHello(){ return new Promise((resolve, reject)=>{ const hello = "hello hello"; resolve(hello) // reject(new Error()); 에러 발생시 }) } sayHello() .then(resolveData => { console.log(resolveData) return resolveData }) .then(resolveData=>{ console.log(resolveData) return resolveData }) .then(resolveData=>{ console.log(resolveData) }) .catch(error=>{ console.log(error) })
프로미스를 사용하면 비동기 처리 후 마치 동기 메서드처럼 값을 반환할 수 있다. sayHello 라는 함수는 프로미스로 비동기적으로 작업이 실행된다. 비동기적으로 처리된 결과 값을 resolve 넘겨주고 sayHello 호출문에서는 then이라는 메서드로 동기적으로 반환 받을 수 있다.
✍ promise 처리 상태
const promise = new Promise((resolve, reject) => { // ... });
pending (대기) 상태 : 비동기 처리 로직이 아직 완료되지 않은 상태
new Promise()생성자를 호출하면 대기 상태가 된다.
function sayHello() { return new Promise((resolve, reject) => { const data = 'hello' resolve(data); }); }
fullfilled (이행) 상태 : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태. 넘겨 받은 인자에서 resolve를 함수로 실행하면 이행 상태가 된다.
function sayHello() { return new Promise((resolve, reject) => { reject(new Error('fail!!!!')); }); } sayHello() .then() .catch((error) => { console.log(error); // fail!!!! });
rejected (실패) 상태 : 비동기 처리가 실패하거나 오류가 발생한 상태. 넘겨 받은 인자에서 reject를 함수로 실행하면 실패 상태가 된다. 실패 상태가 되면 실패 처리의 결과값을 catch()메소드를 통해 받을 수 있다.
const promise = function () {
return new Promise((resolve, reject) => {
resolve("heeeeelo");
});
};
async function sayHello() {
const result = await promise(); // 프로미스 이행 상태가 될 때까지 기다렸다가,
console.log(result); // 완료 되면 하단의 코드가 이어서 실행됨
}
sayHello();
async, await
은 기존의 비동기 처리 방식인 콜백 함수와promise
단점을 보완한 방식이다. promise의 비동기 처리 결과를 then, catch, finally로 후속 처리 없이 동기 처리처럼 사용할 수 있다.
***// 동기처리 //***
setTimeout(()=> {
console.log('5초 끝!')
}, 5000);
setTimeout(()=> {
console.log('10초 끝!')
}, 10000);
function cook(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const myCake = async () => {
await cook(3000);
return '케이크';
};
const myCoffee = async () => {
await cook(2000);
return '커피';
};
const myCookie = async () => {
await cook(5000);
return '쿠키';
};
async function asyncProcess() {
const cake = await myCake();
console.log(cake);
const coffee = await myCoffee();
console.log(coffee);
const cookie = await myCookie();
console.log(cookie);
}
asyncProcess();
***// 비동기처리 //***
setTimeout(()=> {
console.log('5초 끝!')
}, 5000);
setTimeout(()=> {
console.log('10초 끝!')
}, 10000);
function cook(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const myCake = async () => {
await cook(3000);
return '케이크';
};
const myCoffee = async () => {
await cook(2000);
return '커피';
};
const myCookie = async () => {
await cook(5000);
return '쿠키';
};
async function promiseProcess() {
const results = await Promise.all([myCake(), myCoffee(), myCookie()]);
console.log(results);
}
promiseProcess();
await을 많이 사용하면 동기적으로 값을 기다리기 때문에 함수가 그만큼 늦게 끝난다.
Promise.all([sayName(), sayHello(), sayBy()])
를 사용해 all로 전달된 함수를 모두 비동기 실행할 수 있다. 이렇게 비동기로 실행된 결과를 await를 사용해 동기로 받고console.log(results);
를 했다. 만약, Promise.all 앞에 await이 없다면 result는 출력되지 않는다.
✍
async, await
장점
1.promise
결과 값을 then, catch를 통해 다루는 것이 아닌,
변수의 담아 동기적 코드처럼 작성해줄 수 있다는 점에서 편리함을 제공한다.
2.promise catch
가 아닌, 일반적인 코드와 동일한try..catch
사용할 수 있다.
3.promise
의 then 지옥을 피할 수 있다.
✍ async, await 주의
await 키워드는 async 함수 내부에서 사용해야 된다.
await 키워드는 promise 앞에서 사용해야 한다.
async 함수는 반환값을 resolve 하는 promise를 반환한다.
// 그냥 promise 사용
fetch('접속 URL')
.then(function(response) {
console.log(1);
return response.json();
})
.then(json => {
let 지역 = json.find(s => s['시·도별(1)'] == '전국')
console.log(지역)
console.log(지역['1차 접종 누계'])
console.log(지역['2차 접종 퍼센트'])
})
// async, await 사용
async function 접종퍼센트(지역){
const response = await fetch(`접속 URL`);
const data = await(response.json());
const 접종퍼센트 = data.find(s => s['시·도별(1)'] == 지역)['2차 접종 퍼센트'];
console.log(접종퍼센트)
}
접종퍼센트('전국')
위와 같이 동일한 결과를 promise, async, await 2가지 방법으로 받아 올 수 있다.