비동기 프로그래밍은 한 가지 작업이 끝나기 전에 다른 작업을 시작할 수 있는 프로그래밍 방식이다. 이것은 특히 네트워크 요청, 파일 읽기/쓰기, 타이머와 같은 작업에서 중요하다. 이러한 작업은 완료되기까지 시간이 걸리며, 이 시간 동안 프로그램이 대기하면서 다른 작업을 수행할 수 있도록 해주는 것이 바로 비동기 프로그래밍의 핵심이다.
⏲️ setTimeout 함수

⭐ 비동기 asynchronous 코드
--> 동기 synchronous 코드와 달리, 코드가 순서대로 실행되지 않음!

🏃 달리기 경주 예제

setTimeout (() => {
console.log(`🚩 ${num}번 ${name} 도착`);
}, 1000 + Math.random() * 500);
}
Math.random() * 500 은 0 이상 500 미만의 무작위한 숫자를 반환한다. 이는 경주 도착까지의 임의의 시간을 나타낸다. 이 값은 1000에서 1500 사이의 시간에 추가된다. 따라서 전체적으로는 1000에서 2000 사이의 시간 동안의 임의의 시간이 소요된다. 이 코드는 배열의 각 요소에 대해 번호와 이름을 전달하여 doRace 함수를 실행하며, 각 참가자는 무작위로 지정된 시간에 출발하고 도착한다.
⭐ 과정 설명





콜백 지옥 callback hell

💡 연속적으로 비동기 코드를 써야 하는 경우
🏃🏃🏃 릴레이 예제
const DEADLINE = 1500;
function relayRun (name, start, nextFunc, failMag) {
console.log(`${name} 출발`);
const time = 1000 + Math.random() * 500;
setTimeout(()=> {
if(time < DEADLINE){
console.log( `${name} 도착 = ${(start + time)/1000}초`);
nextFunc ?. (start + time);
} else {
console.log(failMag);
console.log(`완주 실패 = ${(start + time)/1000}초`);
}
if(time >= DEADLINE || !Function){
console.log('---경기 종료---');
}
}, time);
}
setTimeout 함수는 두 번째 매개변수로 전달된 시간(밀리초)이 지난 후에 콜백 함수가 실행되도록 스케줄링한다. 이렇게 하면 콜백 함수가 비동기적으로 실행되며, 지정된 시간 이후에 호출된다.
따라서 코드에서}, time);부분은 setTimeout 함수가 time 밀리초 후에 실행되도록 예약하는 역할을 한다. 이렇게 함으로써 경기가 시작된 후 일정한 시간이 지난 후에 선수의 도착을 시뮬레이션하고, 경기의 진행 상황을 효과적으로 제어할 수 있다.
함수를 실행해 보았는데, 작성하기도 어렵고, 가독성이 매우 떨어지는 것을 느낄 수 있다.
프로미스 promise
const myPromise = new Promise((resolve, reject) => {
// 비동기 작업 수행
if (/* 성공 조건 */) {
resolve('성공 결과');
} else {
reject('실패 이유');
}
});
myPromise.then(result => {
console.log('성공:', result);
}).catch(error => {
console.error('실패:', error);
});
예시로 살펴보자

생성자 Promise
then() 메서드는 프로미스가 이행되었을 때 실행된다. resolve() 함수가 실행되면서 넘겨준 값은 .then() 메서드에 전달되어 사용된다. 따라서 result 변수는 resolve() 함수에 전달된 값인 borrow * 1.1을 나타낸다. 그 후에는 .then() 메서드의 콜백 함수가 실행되어 이 값을 처리하고 출력한다.


if (Math.random() < 0.5) { ... } : 무작위로 선택된 확률에 따라 실행될 코드 블록을 결정한다. 여기서는 50%의 확률로 실행된다.reject('사업 망함'): 만약 50%의 확률로 이 코드 블록이 실행된다면, reject 함수가 호출되어 프로미스가 거부된다. '사업 망함'이라는 메시지가 거부 이유로 사용된다. resolve(borrow * 1.1) : 위의 조건문이 실행되지 않았거나, 실행되었지만 거부되지 않은 경우에는 resolve 함수가 호출되어 프로미스가 이행된다. 이때 borrow * 1.1의 결과가 이행 값으로 사용된다. 
생성자 Promise
프로미스 인스턴스의
예제) 💰 10% 이자, 채무자 파산가능성 10%, 5번 빌려주기


이 코드는 moneyLand 함수를 연속적으로 호출하여 금액을 빌리고, 그 금액에 10%를 더한 후에 반복적으로 반환하는 프로세스를 나타낸다.
각각의 .then() 메서드는 프로미스가 이행될 때 실행될 콜백 함수를 등록하며, .catch() 메서드는 프로미스가 거부될 때 실행될 콜백 함수를 등록한다. .finally() 메서드는 프로미스가 이행되든 거부되든 마지막에 실행될 콜백 함수를 등록한다.
각각의 .then() 메서드는 이전 프로미스가 이행된 값을 인자로 받아 moneyLand 함수를 호출하고, 그 반환 값을 다음 .then() 메서드에 전달한다. 이렇게 연속된 호출을 통해 여러 번의 빌림과 반환을 처리한다.
마지막 .then() 메서드에서는 최종 반환된 값을 받아와서 콘솔에 출력한다. .catch() 메서드는 거부된 이유를 받아와서 에러 메시지로 출력한다. 여기서 msg의 값은 reject('채무자 파산')에 해당하는 채무사 파산이다. 마지막으로 .finally() 메서드는 대금업 프로세스의 종료를 알리는 메시지를 출력한다.
🏃🏃🏃 릴레이 예제 프로미스로 구현


50% 확률로 ‘홀’ 또는 ‘짝’을 반환하는 비동기 작업이 있다. 이를 세 번 연속으로 사용하여 그 결과를 쉼표로 구분한 문자열로 받아오려 한다. 예를 들면 ‘홀, 짝, 홀’, ‘짝, 짝, 홀’과 같은 결과가 3초 후 출력되는 것이다.
그럼 먼저, 50% 확률로 홀, 짝을 반환하는 비동기 작업을 수행하는 함수를 만들어보자
function getOddEven(){
return Promise((resolve) => {
setTimeout(() =>{
const res = Math.random() < 0.5 ? '홀' : '짝';
resolve(res);
})
})
}
return Promise((resolve) => { : 새로운 Promise 객체를 반환한다.setTimeout(() => { : setTimeout 함수를 사용하여 비동기적으로 작업을 수행한다. 이 함수는 지정된 시간(여기서는 따로 지정하지 않았으므로 0)이 지난 후에 콜백 함수가 실행된다.const res = Math.random() < 0.5 ? '홀' : '짝'; : Math.random() 함수를 호출하여 50%의 확률로 '홀' 또는 '짝'을 반환resolve(res); : Promise가 이행되고 완료될 때까지 기다리고 있던 resolve 함수를 호출하여 비동기 작업을 완료한다. 여기서는 이 작업의 결과를 res로 전달한다.먼저, promise를 사용해서 결과를 비동기적으로 출력해보자
function concatRes() {
let finalRes = '';
return getOddEven()
.then(res1 => {
finalRes += res1;
return getOddEven();
})
.then(res2 => {
finalRes += ', ' + res2;
return getOddEven();
})
.then(res3 => {
finalRes += ', ' + res3;
return finalRes;
});
}
concatRes().then(res => {
console.log(res);
});
let finalRes = ''; : 결과를 저장할 빈 문자열을 초기화한다.
return getOddEven() : getOddEven 함수를 호출하고 해당 Promise를 반환한다.
.then(res1 => { : 이전 Promise가 이행될 때 실행할 함수를 정의한다. 이 경우 첫 번째 비동기 호출의 결과가 res1 매개변수로 전달된다.
finalRes += res1; : 첫 번째 결과를 finalRes에 추가한다.
return getOddEven(); : 새로운 getOddEven 호출의 Promise를 반환한다.
.then(res2 => { : 새로운 Promise가 이행될 때 실행할 함수를 정의한다.
finalRes += ', ' + res2; : 두 번째 결과를 이전 결과와 쉼표와 함께 finalRes에 추가한다.
return getOddEven(); : 새로운 getOddEven 호출의 Promise를 반환한다.
.then(res3 => { : 새로운 Promise가 이행될 때 실행할 함수를 정의한다.
finalRes += ', ' + res3; : 세 번째 결과를 이전 결과와 쉼표와 함께 finalRes에 추가한다.
return finalRes; : 최종 결과를 포함한 Promise를 반환한다.
이번에는 async & await을 사용해보자
async function concatRes() {
const res1 = await getOddEven();
const res2 = await getOddEven();
const res3 = await getOddEven();
return res1 + ', ' + res2 + ', ' + res3;
}
async function execute() {
const res = await concatRes();
console.log(res);
}
execute();
이 코드는 비동기적으로 세 번의 getOddEven() 호출을 수행하고, 그 결과를 연결하여 반환하는 concatRes 함수와, concatRes 함수를 실행하고 그 결과를 출력하는 execute 함수로 이루어져 있다.
async function concatRes(){
async function execute(){
const res = await concatRes();를 사용하여 concatRes 함수를 실행하고, 해당 결과를 res 변수에 저장한다. 굳이 함수를 실행하고, 출력하는 함수를 따로 만들 필요는 없다!
async function concatRes(){
const res1 = await getOddEven();
const res2 = await getOddEven();
const res3 = await getOddEven();
const results = res1 + ', ' + res2 + ', ' + res3;
console.log(results);
return results
}
concatRes();
위 함수와 같이 결과 값을 console.log()로 출력하고, 반환한다면, execute()함수를 만들 필요 없이, 결과 출력까지 가능하다.
비동기 함수를 사용하여 두 수를 무작위로 생성하고, 그 두 수를 곱한 결과를 출력해보자.
함수를 실행하면 1초 간격으로 두 개의 무작위 수가 생성되고, 그 두 수를 곱한 결과가 출력된다.

randomNum() :
result() :