상세 컨텐츠

본문 제목

[자바스크립트] 섹션 14. 비동기 프로그래밍

[SW]/JavaScript (2025)

by 시원00 2025. 5. 16. 14:19

본문

728x90

제대로 파는 자바스크립트(JavaScript) - by 얄코

(https://www.inflearn.com/course/제대로-파는-자바스크립트/dashboard)

섹션 14. 비동기 프로그래밍

더보기
목차
14-1. 비동기의 개념과 타임아웃
14-2. 프로미스
14-3. 프로미스의 병렬 진행
14-4. async & await
14-5. 네트워크 통신에서의 활용
14-6. 중간점검 퀴즈

14-1. 비동기의 개념과 타임아웃

setTimeout 함수

첫 번째 인자로 넣은 콜백 함수를 두 번째 인자로 넣은 수만큼의 밀리 초 후 실행

Web API, Node.js 등 자바스크립트 환경의 기능 (js 언어의 기능이 아님)

422는 무시

 

비동기 asynchronous 코드

동기 synchronous 코드와 달리 비동기 코드는 순서대로 실행되지 않음

 

일반적으로 타임아웃, 네트워크 요청시간이 걸리는 작업에 사용

당장 할 수 있는 것들을 먼저 다 하고, 시간이 걸리는 작업은 뒤로 넘기기

(만약 비동기 방식이 없다면 데이터를 받아오는 동안 화면이 어는 등 불편함이 생김)

 

delay를 생략하거나 0으로 지정할 경우 "즉시", 더 정확히는 다음 이벤트 사이클에 실행한다.

(현재 실행 중인 모든 코드가 끝난 후, 바로 실행)

 

달리기 경주 예제

1초 ~ 1.5초 사이 무작위 시간 안에 도착

 

과정 설명

1. 실행 시점에 도착하면 동기 코드 실행

2. 비동기 콜백 코드는 환경(Web API 등)으로 넘겨줌

    2-1. 주어진 작업을 마친 작업들은 태스크 큐 task queue 선로로 진입

    2-2. 이벤트 루프 event loop: 태스크 큐의 작업이 도착하는대로 자바스크립트 실행도로에 올려놓음

 

자바스크립트의 코드는 싱글 스레드로 실행

자바스크립트의 실행 환경 멀티 스레드 (여러 작업들이 동시다발적으로 진행될 수 있음)

* 비동기 콜백 코드는 동기 코드가 모두 실행된 다음 진행됨 (지연 시간을 0으로 해도 나중에 실행되는 이유)


14-2. 프로미스

콜백 지옥 callback hell

연속적으로 비동기 코드를 써야하는 경우 콜백 함수 안에 또 다른 콜백 함수를 넣어야하는 상황 발생 (→ 콜백 지옥)

횟수가 많을수록 가독성도 낮아지고 직관성이 떨어짐

186은 무시. 0.5초 간격으로 1, 2, 3, 4, 5 출력

 

릴레이 예제 (콜백 지옥)

철수, 영희, 돌준, 정아, 길돈이 차례로 이어달리기하는 코드

- 각 골인 시간 기록: 이전 콜백함수의 결과가 다음 콜백함수로 넘겨져 축적됨

- 한 주자라도 데드라인(밀리초)을 넘기면 실패: 주자마다 다른 실패 메시지 출력

- 완주 실패 시 '완주 실패 - ${전체 소요시간}' 출력

- 마지막에 '--경기 종료--' 출력

 

프로미스 promise

어떤 과정 이후 주어진 동작을 실행할 것이란 약속

중첩된 비동기 코드를 직관적이고 연속적인 코드로 작성할 수 있도록 함

 

생성자 Promise

- 새로운 약속을 하는 코드

- 인자로 받는 콜백함수의 첫 번째 인자 resolve(이름은 관례): 약속 이행 성공 시, 반환할 값 넣어 실행

- 인자로 받는 콜백함수의 두 번째 인자 reject(이름은 관례): 약속 이행 실패 시, 반환할 값 넣어 실행

- reject가 실행되면 resolve는 무시됨

 

프로미스 인스턴스의 메서드

- then: resolve를 통해 반환된 결과를 인자로 하는 콜백 함수를 넣음

- catch: reject를 통해 (실패로 인해) 반환된 결과를 인자로 하는 콜백 함수를 넣음

- finally: 성공하든 실패하든 실행할 콜백 함수 (필요할 때만 사용)

- 또 다른 프로미스를 반환 (체이닝 가능)

 

일반적으로 내부에 비동기 코드를 사용

시간이 소모되는 비동기 과정 후 ...을 반환하겠다는 약속

 

생성자 Promise

- 새로운 약속을 하는 코드

- 인자로 받는 콜백함수의 첫 번째 인자 resolve(이름은 관례): 약속 이행 성공 시, 반환할 값 넣어 실행

- 인자로 받는 콜백함수의 두 번째 인자 reject(이름은 관례): 약속 이행 실패 시, 반환할 값 넣어 실행

- reject가 실행되면 resolve는 무시됨

 

프로미스 인스턴스의 메서드

then: resolve를 통해 반환된 결과를 인자로 하는 콜백 함수를 넣음

catchreject를 통해 (실패로 인해) 반환된 결과를 인자로 하는 콜백 함수를 넣음

finally성공하든 실패하든 실행할 콜백 함수 (필요할 때만 사용)

- 또 다른 프로미스를 반환 (메서드 체이닝으로 사용)

 

 

then은 연속적으로 메서드 체이닝 가능

 

예제 - 10% 이자, 채무자 파산 가능성 10%, 5번 빌려주기(프로미스로 구현)

성공 케이스
실패 케이스

 

예제 - 릴레이(프로미스로 구현)

실패 케이스
성공 케이스


14-3. 프로미스의 병렬 진행

예제 - 다섯 주자들이 동시에 질주. 데드라인(밀리초) 내에 들어오지 못하면 탈락.

 

여러 프로미스를 병렬 처리하기 위한 Promise의 정적 메서드들

1. all

- 프로미스의 배열을 받아 동시에 진행

- 모두 성공하면 resolve된 값들을 배열로 반환 (then으로 받음)

- 하나라도 실패하면 catch 실행

실패 케이스
성공 케이스

 

성공 시 탑3 표시하기

성공 시 top3 표시
실패 시 바로 경기 종료

 

2. allSettled

- 주어진 프로미스들의 결과를 배열로 출력

- 실패 유무 관계없이 then으로 배열 반환

 

 

3. any

- 가장 먼저 성공한 프로미스의 결과를 then으로 반환

- 모두 실패 시 오류 발생

가장 먼저 성공 한 프로미스의 결과 반환
모두 실패 시 오류 발생

 

4. race

- 성공이든 실패든 첫 결과물 then 또는 catch로 반환

첫 결과물 실패 -> catch로 반환
첫 결과물 성공 -> then으로 반환


14-4. async & await

async 함수

- 프로미스를 기반으로 동작

- 마치 동기 코드처럼 직관적으로 코딩할 수 있음

 

await: 코드의 진행을 멈추고 프로미스로부터 답을 받아냄

await은 async 함수 또는 모듈 내에서만 사용 가능

 

예제 - 10% 이자, 채무자 파산 가능성 10%, 5번 빌려주기(async/await)

일반 Promise 문보다 가독성 좋음

reject 가능성이 있는 경우 try ... catch ... finally 문 사용

성공 케이스
실패 케이스

 

예제 - 릴레이(async/await)

성공 케이스
실패 케이스


14-5. 네트워크 통신에서의 활용

예제용 서버 요청

링크로 접속하면 아래 파일이 다운로드됨

1. 경기 결과

https://showcases.yalco.kr/javascript/mockserver/race-result
[
  { "runner_idx": 1,
    "record": 1454.3881
  },
  { "runner_idx": 2,
    "record": 1198.4922
  },
  { "runner_idx": 3,
    "record": 1404.1087
  },
  { "runner_idx": 4,
    "record": 1218.7154
  },
  { "runner_idx": 5,
    "record": 1374.5151
  }
]

2. 각 선수들 정보 (예시: 1)

https://showcases.yalco.kr/javascript/mockserver/runners/{1~3}
{
  "idx": 1,
  "name": "홍길동",
  "school_idx": 2,
  "grade": 3
}

3. 학교 정보 (예시: 1)

https://showcases.yalco.kr/javascript/mockserver/schools/{1~3}
{
  "idx": 1,
  "name": "복근대학교",
  "majors": ["육상", "체조", "투포환"]
}

 

Fetch API

Web API에서 제공하는 기능 (브라우저 제공)

네트워크로부터 리소스를 받아오기 위한 다양하고 강력한 기능들 제공

이전 방법: HTMLHttpRequest

 

fetch 메서드

네트워크 통신으로 원격에 요청을 보내고 답을 받아오는 프로미스를 반환

 

반환되는 결과: { response }
요청의 결과에 대한 정보들을 담은 객체

json 메서드: 결과의 body로 받은 텍스트를 JSON 객체로 변환하여 반환

 

에러 상황 시(잘못된 주소 등) catch에서 처리

 

예제용 파일을 json으로 변경한 결과 미리보기

 

연속 fetching 예제

경기 결과를 받아온 뒤 1등 주자 선택

→ 해당 주자의 상세정보를 반아온 뒤 학교 코드 추출

→ 해당 학교의 정보 받아오기

 

1. 프로미스 형태로 구현

 

2. async, await으로 구현


14-6. 중간점검 퀴즈

1. 50% 확률로 ‘홀’ 또는 ‘짝’을 반환하는 비동기 작업이 있습니다. 이를 세 번 연속으로 사용하여 그 결과를 쉼표로 구분한 문자열로 받아오려 합니다. 예를 들면 ‘홀, 짝, 홀’, ‘짝, 짝, 홀’과 같은 결과가 3초 후 출력되는 것입니다. 이와 같은 작업을 콜백이 중첩된 코드로 작성해보세요.

예상 답:

function getOddOrEven() {
    if (Math.random() < 0.5) {
        console.log('홀');
    } else {
        console.log('짝');
    }
}

setTimeout(() => {
    getOddOrEven();
    setTimeout(() => {
        getOddOrEven();
        setTimeout(() => {
            getOddOrEven();
        }, 3000);
    }, 3000);
}, 3000);

결과:

 

정답:

function getOddEven(callback) {
    setTimeout(() => {
        const res = Math.random() < 0.5 ? '홀' : '짝';
        callback(res);
    }, 1000);
}

function concatRes(callback) {
    getOddEven((res1) => {
        getOddEven((res2) => {
            getOddEven((res3) => {
                const finalRes = res1 + ', ' + res2 + ', ' + res3;
                callback(finalRes);
            });
        });
    });
}

concatRes((res) => {
    console.log(res);
});

결과:

 

2. 위 1번의 코드를 Promise를 사용해서 다시 작성해보세요.

예상 답:

function getOddOrEven() {
    return new Promise((resolve)=> {
        setTimeout(() => {
            if (Math.random() < 0.5) {
                resolve('홀');
            } else {
                resolve('짝');
            }
        }, 3000);
    });
}

getOddOrEven()
.then(console.log)
.then(getOddOrEven)
.then(console.log)
.then(getOddOrEven)
.then(console.log);

 

정답:

function getOddEven() {
    return new Promise((resolve) => {
        setTimeout(() => {
            const res = Math.random() < 0.5 ? '홀' : '짝';
            resolve(res);
        }, 1000);
    });
}

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);
});

 

3. 이번에는 async & await을 사용하는 코드로 다시 작성해보세요.

예상 답:

function getOddOrEven() {
    return new Promise((resolve)=> {
        setTimeout(() => {
            if (Math.random() < 0.5) {
                resolve('홀');
            } else {
                resolve('짝');
            }
        }, 3000);
    });
}

const a = await getOddOrEven();
console.log(a);
const b = await getOddOrEven();
console.log(b);
const c = await getOddOrEven();
console.log(c);

 

정답:

function getOddEven() {
    return new Promise((resolve) => {
        setTimeout(() => {
            const res = Math.random() < 0.5 ? '홀' : '짝';
            resolve(res);
        }, 1000);
    });
}

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();

 

 

FIN.

728x90

관련글 더보기

댓글 영역