[JavaScript] 비동기

FMA·2024년 12월 15일

HTML/CSS/JS

목록 보기
8/9

동기(Synchronos) vs 비동기(Asynchronos)

동기 : 일을 순차적으로 처리하는 것, 전 일의 완료 시점과 다음 일의 시작 시점이 같다.

비동기 : 전 일의 완료 시점과 다음 일의 시작 시점이 같을 필요가 없다.

그림으로 나타내면 다음과 같다.

위 그림에서 동기 방식에 비해 비동기 방식이 같은 일을 하는데 걸리는 시간이 훨씬 짧은 것을 알 수 있다.

쉬운 예시를 들자면, 커피를 주문을 받는 과정을 생각해볼 수 있다.

만약 커피 주문이 동기적으로 작용한다면, 다음과 같은 과정을 거친다.

  1. 손님 1이 아메리카노를 주문한다.
  2. 접수를 받은 직원이 아메리카노를 건넨다.
  3. 직원이 손님 1에게 아메리카노를 전달한다.
  4. 손님 2가 카페라떼를 주문한다.
  5. 접수를 받은 직원이 카페라떼를 만든다.
  6. 직원이 손님 2에게 카페라떼를 전달한다.

이렇게 되면 손님 2는 손님 1이 아메리카노를 전달받을 때까지 주문을 하지 못하고 대기열에 머물러 있어야 한다.

한편 커피 주문이 비동기적으로 작용한다면, 다음과 같은 방식으로 동작한다.

  1. 손님 1이 아메리카노를 주문한다.
    1. 접수를 받은 직원이 아메리카노를 내린다.
    2. 아메리카노가 완성되면 직원이 손님 1을 부른다.
    3. 아메리카노를 손님 1에게 건넨다.
  2. 손님 2가 카페라떼를 주문한다.
    1. 접수를 받은 직원이 카페라떼를 내린다.
    2. 카페라떼가 완성되면 직원이 손님 2를 부른다.
    3. 카페라떼를 손님 2에게 건넨다.

이 경우 손님 2는 손님 1이 아메리카노를 받을 때까지 주문을 기다릴 필요가 없다.

비동기(Asynchoros)를 사용하는 이유

웹사이트 등은 대부분 클라이언트와 서버 간의 통신에 의해 작동한다.

  • 클라이언트 : 서버로 접속하는 컴퓨터 (보통 우리가 접속하는 컴퓨터라고 생각해도 무방하다.)
  • 서버 : 서비스, 리소스 등을 제공하는 컴퓨터 (웹 서버, 게임 서버 등)

이렇듯 클라이언트와 서버가 통신할 때 동기 방식을 사용할 때보다 비동기 방식을 사용할 때의 장점이 확연히 드러난다.

그림에서 확인할 수 있듯, 같은 시간 동안 클라이언트가 상대적으로 훨씬 더 많은 일을 처리할 수 있게 된다.

서버의 응답을 가만히 기다리는 시간 동안 다른 일을 처리할 수 있기 때문이다.

Callback

const printString = (string) => {
    setTimeout (
        () => {
            console.log(string)
        },
        Math.floor(Math.random() * 100) + 1
    )
}

const printAll = () => {
    printString("A")
    printString("B")
    printString("C")
}

printAll()

위 코드는 랜덤한 시간 이후에 문자 'A', 'B', 'C'를 각각 출력하는 비동기 방식의 코드이다.

위 코드는 A, B, C의 순서가 랜덤으로 출력되며, 해당 순서를 조절할 수는 없다.

그렇다면 A, B, C의 출력 순서를 조절하려면 어떻게 해야 할까?

const printString = (string, callback) => {
    setTimeout (
        () => {
            console.log(string)
            callback()
        },
        Math.floor(Math.random() * 100) + 1
    )
}

const printAll = () => {
    printString("A", () => {
        printString("B", () => {
            printString("C", () => {})
        })
    })
}

printAll()

이는 callback 함수를 통해 해결할 수 있다.

callback 함수 인자로서 다음으로 출력할 함수를 전달하는 방식이다.

그러나 이는 동기로 실행할 함수가 많아질수록 계속 함수가 밀린다는 문제가 있다. (callback hell)

이를 해결하기 위해 Promise 방식을 이용한다.

Promise

const printString = (string) => {
	return new Promise((resolve, reject) => 
	    setTimeout (
	        () => {
	            console.log(string)
	            callback()
	        },
	        Math.floor(Math.random() * 100) + 1
	    )
     })
}

const printAll = () => {
    printString("A")
    .then(() => {
	    return printString("B")
    })
    .then(() => {
	    return printString("C")
    })
}

printAll()

함수 선언 시 callback 함수를 받지 않고, return문에 Promise 함수를 사용한다.

그러면 함수의 실행 순서를 .then을 사용해 조절할 수 있다.

Promise - then은 js 내장 함수로, .then()으로 작업 성공 시의 task, .catch()으로 작업 실패 시의 task를 따로 지정해줄 수 있다.

자세한 내용은 #Promise.prototype.then 링크에서 확인할 수 있다.

async/await

function printA() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('A')}, 100)
    })
}

function printB() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('B')}, 100)
    })
}

function printC() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('C')}, 100)
    })
}

function printD() {
    return new Promise((resolve, reject) => {
        setTimeout(() => { resolve('D')}, 100)
    })
}

const result = async () => {
    const one = await printA();
    console.log(one)

    const two = await printA();
    console.log(two)

    const three = await printA();
    console.log(three)

    const four = await printA();
    console.log(four)
}

result();

promise와 같은 구조, 표기만 조금 다른 것.

비동기함수를 동기함수인것처럼 실행한다.

0개의 댓글