[JavaScript] 동기 비동기, 콜백 함수

윤지·2024년 11월 6일

JavaScript

목록 보기
25/30
post-thumbnail

1. 동기적 작업의 문제점

자바스크립트 동기: 한 작업이 완료될 때까지 다음 작업을 기다리게 하는 방식

자바스크립트의 싱글 스레드 특성으로 인해, 오래 걸리는 동기 작업은 다른 모든 작업을 지연시킴

예: 긴 루프나 대기 시간이 있는 작업 실행 중 브라우저 응답 불가

예제

console.log("시작");

for (let i = 0; i < 1000000000; i++) {
  // 긴 루프
}

console.log("끝");

실행 순서

  1. "시작" 출력
  2. 매우 긴 루프 실행 (이 과정에서 브라우저 응답 불가)
  3. "끝" 출력

이 예제에서 긴 작업이 다른 작업을 차단하여 프로그램 전체가 멈춥

⇒ 동기적 방식으로 긴 작업 처리는 사용자 경험을 저하시킴

비동기 작업 처리 방법

자바스크립트는 이러한 문제를 해결하기 위해 비동기 처리 방식을 지원함. 비동기 방식으로 코드를 작성하면 긴 작업이 있어도 다른 작업이 먼저 실행될 수 있도록 도움

1. 웹 API

  • 브라우저가 제공하는 API로, 자바스크립트가 비동기 작업을 처리할 수 있도록 돕는 환경을 제공
  • setTimeout 같은 함수들은 브라우저의 웹 API로, 실제 작업을 수행한 후, 콜백 함수를 자바스크립트 엔진으로 다시 전달하여 실행하게 함
console.log("시작");

setTimeout(() => {
  console.log("비동기 작업 완료");
}, 2000);

console.log("끝");

실행 순서

  1. "시작""끝"은 동기적으로 실행됨
  2. setTimeout은 2초 후 실행되도록 예약되며, 콜백 함수는 태스크 큐에 추가됨
  3. 이벤트 루프는 콜 스택이 비워지면 태스크 큐의 콜백을 가져와 실행함

이 예제에서 자바스크립트는 setTimeout의 콜백을 비동기로 처리하여 "끝"을 먼저 출력함

2. 콜백 함수

  • 콜백 함수: 다른 함수에 매개변수로 전달되어 그 함수가 실행되는 동안 특정 시점에 호출되는 함수
  • 예: setTimeout, setInterval, 이벤트 리스너
// 'callback'이라는 이름은 관용적으로 많이 사용됨

function a(callback) {
    setTimeout(() => {
        console.log("a");
        callback();
    }, 0);
}

function b() {
    console.log("b");
}

a(b);

화살표 함수의 탄생 배경

화살표 함수의 도입 목적

  • 콜백 문법의 간결화
  • 중첩된 콜백의 가독성 개선

콜백 함수 사용 예제 비교

function task1(callback) {
    console.log("task1");
    callback();
}

function task2() {
    console.log("task2");
}

기존 콜백 함수

// 함수 호출
task1(function() {
    task2();
});

화살표 함수를 사용한 콜백

// 함수 호출: 화살표 함수로 간결하게 표현
task1(() => {
    task2();
});

콜백을 사용하여 순서 보장하기

콜백을 사용하면 작업 순서 보장 가능. 각 작업 완료 후 콜백 호출하여 다음 작업 시작하면 순차적 실행 가능

아래 코드는 여러 콜백 함수를 연달아 사용하는 '콜백 드리블' 방식의 예시. 이 방법은 작업들을 순차적으로 연결

function task1(callback) {
    console.log("task1");
    callback();
}

function task2(callback) {
    console.log("task2");
    callback();
}

function task3(callback) {
    setTimeout(() => {
        console.log("task3");
        callback();
    }, 0);
}

function task4() {
    console.log("task4");
}

// 함수 호출
task1(() => {
    task2(() => {
        task3(() => {
            task4();
        });
    });
});

전체 코드 실행 순서

이 코드는 콜백 함수를 이용해 작업의 순서를 보장. 실행 순서는 다음과 같음:

  1. task1 실행 → "task1" 출력 → task2 호출
  2. task2 실행 → "task2" 출력 → task3 호출
  3. task3 실행 → setTimeout에 의해 비동기 처리 → 태스크 큐에 추가
  4. 콜 스택이 비워진 후 → "task3" 출력 → task4 호출
  5. task4 실행 → "task4" 출력 → 전체 작업 완료

이러한 방식으로 콜백 함수를 사용하면 비동기 작업을 포함한 모든 작업이 원하는 순서대로 실행됨

결과

task1
task2
task3
task4

콜백이 없는 경우의 문제

function task1() {
    console.log("task1");
}

function task2() {
    console.log("task2");
}

function task3() {
    setTimeout(() => {
        console.log("task3");
    }, 0);
}

function task4() {
    console.log("task4");
}

task1();
task2();
task3();
task4();

task3은 비동기 함수로, 0초 지연 설정에도 불구하고 태스크 큐에 추가되어 대기함.

따라서 다른 동기 작업들이 먼저 실행된 후에 처리되어 다음과 같은 순서로 출력

task1
task2
task4
task3

이렇게 되면 원하는 순서와 다르게 task3이 마지막에 출력됨 ⇒ 비동기 함수가 순서를 흐트러뜨린 것

콜백지옥

콜백 함수는 비동기 작업의 순차 실행에 유용하지만, 중첩이 많아지면 가독성이 떨어지는 '콜백 지옥' 문제가 발생함

중첩된 콜백은 코드를 복잡하게 만들고 유지보수를 어렵게 함. 이 문제 해결을 위해 Promise가 도입됨

// 함수 호출
task1(() => {
  task2(() => {
    task3(() => {
      task4();
    });
  });
});
profile
프론트엔드 공부 기록 아카이빙🍁

0개의 댓글