[JavaScript 문법] 동기, 비동기

조아영·2024년 7월 30일

◼ 동기(동시에 일어나는)

synchronous : 동시에 일어나는

  • 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 순차적 진행방식
  • CPU의 계산에 의해 즉시 처리가 가능한 대부분의 코드
  • 계산이 복잡해서 CPU가 계산하는 데에 오래 걸리는 코드

◼ 비동기(동시에 일어나지 않는)

a + synchronous = async : 동시에 일어나지 않는

  • 실행 중인 코드의 완료 여부와 무관하게, 즉시 다음 코드로 넘어가는 방식
  • setTimeout, addEventListner 등...
  • 별도의 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드
  • 복잡도가 올라갈 수록 비동기적 코드의 비중이 늘어납니다

◼ 비동기 작업의 동기적 표현

비동기적 요소를 동기적으로 바꾸려는 이유는
순서를 보장할 수 없으니 순서보장이 필요한 로직에서 순서를 보장 받기 위해서 입니다.

Promise

Promise는 비동기 처리가 끝나면 알려달라는 '약속'입니다. new 연산자로 호출하고 Promise의 인자로 넘어가는 콜백은 바로 실행됩니다.
그 내부의 resolve(또는 reject) 함수를 호출하는 구문이 있을 경우 resolve(또는 reject) 둘 중 하나가 실행되기 전까지는 다음(then), 오류(catch)로 넘어가지 않습니다.
따라서, 비동기작업이 완료될 때 비로소 resolve(해결하다), reject(거부하다)를 호출합니다.

new Promise(function (resolve) {
    setTimeout(function () {
        var name = "에스프레소";
        console.log(name);
        resolve(name);
    }, 500);
})
    .then(function (prevName) {
        // 안에서도 새롭게 promise를 만듬
        return new Promise(function (resolve) {
            setTimeout(function () {
                var name = prevName + ", 아메리카노";
                console.log(name);
                resolve(name);
            }, 500);
        });
    })
    .then(function (prevName) {
        return new Promise(function (resolve) {
            setTimeout(function () {
                var name = prevName + ", 카페모카";
                console.log(name);
                resolve(name);
            }, 500);
        });
    })
    .then(function (prevName) {
        return new Promise(function (resolve) {
            setTimeout(function () {
                var name = prevName + ", 카페라떼";
                console.log(name);
                resolve(name);
            }, 500);
        });
    });

// 리팩토링 : 위 예제의 반복부분을 함수화 한 코드
var addCoffee = function (name) {
    return function (prevName) {
        return new Promise(function (resolve) {
            setTimeout(function () {

                var newName = prevName ? prevName + ", " + name : name;
                console.log(newName);
                resolve(newName);
            }, 500);
        });
    };
};

addCoffee("에스프레소")()
    .then(addCoffee("아메리카노"))
    .then(addCoffee("카페모카"))
    .then(addCoffee("카페라떼"));

Generator

*가 붙은 함수(function*)가 제너레이터 함수입니다. 제너레이터 함수는 실행 시, Iterator 객체를 반환합니다.

이터러블 객체(Iterable : 반복될 수 있는, 반복할 수 있는)
iterator 객체는 next라는 메서드를 가지고 있고 next 메서드로 순환 할 수 있는 객체입니다.
next 메서드 호출 시, Generator 함수 내부에서 yield(양보하다, 미루다) → stop을 실행합니다. 이후 다시 next 메서드를 호출하면 멈췄던 부분에서 그 다음의 yield까지 실행 후 stop합니다.
즉, 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위에서 아래로 순차적으로 진행됩니다.

// 제너레이터 함수 안에서 쓸 addCoffee 함수 선언
var addCoffee = function (prevName, name) {
    setTimeout(function () {
        coffeeMaker.next(prevName ? prevName + ", " + name : name);
    }, 500);
};

// 제너레이터 함수 선언
// yield 키워드로 순서 제어
var coffeeGenerator = function* () {
    // yield addCoffee : addCoffee 함수 안의 next를 만날때까지 수행
    var espresso = yield addCoffee("", "에스프레소");
    console.log(espresso);
    // 앞 순서가 끝날때까지 기다렸다가 수행
    var americano = yield addCoffee(espresso, "아메리카노");
    console.log(americano);
    var mocha = yield addCoffee(americano, "카페모카");
    console.log(mocha);
    var latte = yield addCoffee(mocha, "카페라떼");
    console.log(latte);
};
var coffeeMaker = coffeeGenerator(); // 이터레이터 객체 반환
coffeeMaker.next(); // 실행 → coffeeGenerator 내부로 진입

Promise + async/await

async(비동기), await(기다리다)는 ES2017에 새롭게 추가됐습니다.
비동기 작업을 수행할 함수 앞에 async를 붙여주고 함수 내부에서 실질적인 비동기 작업이 필요한 위치마다 await를 붙여주면 됩니다.
Promise ~ then과 동일한 효과를 얻을 수 있습니다.

// coffeeMaker함수에서 호출 할 함수. Promise를 반환 함
var addCoffee = function (name) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(name);
        }, 500);
    });
};

// 
var coffeeMaker = async function () {
// 화살표 함수일땐
// var coffeeMaker = async () => {
    var coffeeList = "";
    var _addCoffee = async function (name) {
        coffeeList += (coffeeList ? ", " : "") + (await addCoffee(name));
    };

    // promise를 반환하는 함수인 경우, await를 만나면 무조건 끝날 때까지 기다림
    await _addCoffee("에스프레소");
    console.log(coffeeList);
    await _addCoffee("아메리카노");
    console.log(coffeeList);
    await _addCoffee("카페모카");
    console.log(coffeeList);
    await _addCoffee("카페라떼");
    console.log(coffeeList);
};

// 호출
coffeeMaker();

0개의 댓글