비동기와 동기

하정현·2023년 10월 23일

JS

목록 보기
6/9

Javascript에서 동기적 방식이란
현재 실행 중인 코드가 끝나고 다음 코드가 실행되는 방식이다.
즉시 처리가 가능한 대부분의 코드, 계산이 복잡해서 cpu가 계산하는데
오래걸리는 코드 역시 동기적 코드이다.

비동기 방식은 실행 중인 코드의 완료여부와 무관하게 즉시 다음 코드로 넘어간다.
순서 보장받지 못하고, 함수의 제어권이 언제올지 모른다.
그래서 순서를 보장받기 위해서 동기적으로 만들어 줘야 한다.
웹의 복잡도가 올라갈 수록 비동기적 코드 비중이 늘어난다.

그러면 비동기 작업의 동기적 표현은 뭘로 할수 있을까?
Promise, Generator, async/await를 활용해보자.

Promise

new 연산자로 호출 -> 인자로 넘어가는 콜백은 바로 실행됨
내부 resolve, reject(=try catch와 비슷한 것 같다.)

then과 catch로 실행순서를 보장한다.
일단 밑에 코드에서는 실패하는 경우는 빼고 resolve, then만 사용한다

new Promise(function (resolve) { //인자로 resolve, reject 
    setTimeout(function () {
        var name = "에스프레소";
        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);
    })
}).then(function (prevName) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            var name = prevName + ", 카페라떼";
            console.log(name);
            resolve(name);
        }, 500);
    })
})


커피가 순서대로 출력되는 로직을 만들때 Promise 연산자와 then을 통해 순서를 보장했다.


Refactoring을 통해 반복되는 부분을 함수화 시켜 보자

var addCoffee = (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("카페라떼"));

코드가 훨씬 간결해졌다.


이터러블 객체(iterable)
제너레이터는 반복할수 있는 iterator 객체를 생성한다.
iterator 객체는 next를 가지고 있음
순환하면서 수행하기 용이하다. yield를 통해 순차적으로 실행 가능하게 한다.

비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면
generator 내부 소스가 순차적으로 실행 된다

var addCoffee = function (prevName, name) {
	setTimeout(function () {
		coffeeMaker.next(prevName ? prevName + ', ' + name : name);
	}, 500);
};


var coffeeGenerator = function* () {             // *가 붙은 함수가 제너레이터 실행시 Iterator 객체를 반환
	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();

다음은 Promise를 이용하지만
then 이 아닌 async(비동기)/ await(기다리다)로 바꾸는 법

함수앞에 async를 붙여주게 되면 {} 괄호 안에 await를 만난 메서드는
그 메서드가 끝날때 까지 무조건 기다리게 된다.
그리고 그 메서드는 항상 Promise를 반환한다.
Promise를 반환하는 함수인 경우에도 awiat를 만나면 작업이 완료되기 까지 기다린다.

var addCoffee = function (name) {
	return new Promise(function (resolve) {
		setTimeout(function(){
			resolve(name);
		}, 500);
	});
};


var coffeeMaker = async function () {
	var coffeeList = '';
	var _addCoffee = async function (name) {
		coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
	};
	await _addCoffee('에스프레소');
	console.log(coffeeList);
	await _addCoffee('아메리카노');
	console.log(coffeeList);
	await _addCoffee('카페모카');
	console.log(coffeeList);
	await _addCoffee('카페라떼');
	console.log(coffeeList);
};
coffeeMaker();

이젠 비동기 작업을 동기적으로 만드는 법에 대해 어느정도 이해가 되는 것 같다.
처음 들었을 때에는 머리에 들어오지 않았지만 정리하면서 반복해서 듣다보니 알겠다.
근데 내가 생각하기에 아는 것에서 그치지 않고 실제로 적용해봐야 100% 내것이 되는것 같다.

0개의 댓글