dh_0e

[JavaScript] 주요 문법 정리(2) (콜백 함수, 동기 vs 비동기, 비동기 작업의 동기적 표현) 본문

내일배움캠프/HTML,CSS,JavaScript

[JavaScript] 주요 문법 정리(2) (콜백 함수, 동기 vs 비동기, 비동기 작업의 동기적 표현)

dh_0e 2024. 4. 25. 18:50

 

콜백 함수

다른 함수(고차 함수) 또는 메소드에게 인자로 넘겨줌으로써 자신의 제어권도 함께 넘겨줌
ex) setInterval( function(){}, time ) 콜백함수의 제어권을 넘겨받아 time마다 콜백함수의 로직을 수행

      Object.foreach(function(A){}) 콜백함수의 제어권을 넘겨받아 A의 요소들로 콜백함수의 로직을 수행

 

동기 vs 비동기

동기(synchronous) - 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식
비동기(a+synchronous => async) - 실행중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
ex) setTimeout, addEventListner
별도의 요청, 실행 대기, 보류 등과 관련된 코드 모두 비동기적 코드임

비동기 작업의 부작용
순서를 보장하지 않으며, 순서를 보장하려다(동기화하려다) 콜백지옥을 발생시킴

 

콜백지옥이란?

다중첩 for문 느낌으로 콜백함수를 익명함수로 전달하는 과정이 반복되어 들여쓰기가 여러번 되는 것

이벤트 처리, 서버 통신과 같은 비동기적 작업을 수행할 때 발생하며 가독성이 매우 떨어지며 수정이 어려움

setTimeout(
    function (name) {
        var K_noodle = name;
        console.log(K_noodle);
        setTimeout(
            function (name) {
                K_noodle += ", " + name;
                console.log(K_noodle);
                setTimeout(
                    function (name) {
                        K_noodle += ", " + name;
                        console.log(K_noodle);
                        setTimeout(
                            function (name) {
                                K_noodle += ", " + name;
                                console.log(K_noodle);
                            },
                            500,
                            "신라면"
                        );
                    },
                    500,
                    "안성탕면"
                );
            },
            500,
            "진라면"
        );
    },
    500,
    "삼양라면"
);

 

 

비동기 작업의 동기적 표현

1. promise - 비동기 처리에 대해 처리가 끝나면 알려달라는 약속
 new 연산자로 호출한 Promise의 인자로 넘어가는 콜백은 바로 실행됨
내부의 resolve(or reject) 함수를 호출하는 구문이 있을 경우 resolve(or reject) 둘 중 하나가 실행되기 전까지 다음(then), 오류(catch)로 넘어가지 않음 / resolve(or reject)가 실행되었어도 바로 넘어가지 않고 함수가 끝나면 넘어감

new Promise(function (resolve, reject) {
    setTimeout(function () {
        var name = '삼양라면';
        console.log(name);
        resolve(name);
        console.log("진라면보다 먼저 나옴"); // resolve를 받아도 함수가 끝나야 넘어감
    }, 500);
}).then(function (prevName) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            var name = prevName + ', 진라면';
            console.log(name);
            resolve(name);
        }, 500);
    });
}).then(function (prevName) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            var name = prevName + ', 안성탕면';
            console.log(name);
            resolve(name);
        }, 500);
    });
});

 

아래는 reject 발생 시 .then.catch(err)로 에러를 전달하는 코드 예시

function getData(response) {
    return new Promise(function (resolve, reject) {
        if (response) {
            resolve(response);
        }
        reject("No Data");
        console.log("제일 먼저 출력됨"); // reject를 받아도 함수가 끝나야 넘어감
    });
}

getData("Put data here").then(function (data) {
    console.log(data); // response 값 출력
}).catch(function (err) {
    console.error(err); // Error 출력
});



2. 제너레이터 함수를 사용한 반복자(Iterator)

 제너레이터 함수는 *가 붙은 함수이며 (function* () ) 실행하면, Iterator 객체가 반환(next()를 가지고 있음)

Iterator 객체는 next 메서드로 순환하며, 처음 next 메서드 호출 시, Generator 함수 내부에서 처음부터 시작하여 가장 먼저 등장하는 yield에서 멈추며, 다시 next 메서드를 호출하면 그 다음의 yield까지 실행 후 멈춤

정리하자면 비동기 작업이 완료되는 시점(yield)마다 next 메서드를 호출하여 Generator 함수가 순차적으로 진행하게 함

var addK_noodle = function (prevName, name) {
	setTimeout(function () {
		noodleMaker.next(prevName ? prevName + ', ' + name : name);
	}, 500);
};
var noodleGenerator = function* () {
	var Samyang = yield addK_noodle('', '삼양라면');
	console.log(Samyang);
	var Gin = yield addK_noodle(Samyang, '진라면');
	console.log(Gin);
	var Anseong = yield addK_noodle(Gin, '안성탕면');
	console.log(Anseong);
	var Shin = yield addK_noodle(Anseong, '신라면');
	console.log(Shin);
};
var noodleMaker = noodleGenerator();
noodleMaker.next();

 

이때 next()의 매개변수는 yield로 전달하며, yield를 만나 next()로 돌아갈 땐 value에 값을 담아 보냄

function* gen() {
  while (true) {
    let value = yield "here is the value";
    console.log(value);
  }
}

const g = gen();

console.log(g.next(100)); // gen 함수의 시작부터 yield 까지 이동, 100은 콘솔 안 찍힘
// { value: 'here is the value', done: false }
console.log(g.next(1));
// 1
// "{ value: 'here is the value', done: false }"
console.log(g.next(2).value);
// 2
// here is the value

 

 

3. async(비동기) / await(기다리다)
async 함수 안에서 await를 만나면 함수가 무조건 끝날 때 까지 기다린다. 이때 함수는  promise(resolve or reject)를 반환해야만 하며 반환한 값은 await로 전달됨

var addK_noodle = function (name) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(name);
        }, 500);
    });
};
var noodleMaker = async function () {
    var noodleList = '';
    var _addK_noodle = async function (name) {
        noodleList += (noodleList ? ', ' : '') + await addK_noodle(name);
    };
    await _addK_noodle('삼양라면');
    console.log(noodleList);
    await _addK_noodle('진라면');
    console.log(noodleList);
    await _addK_noodle('안성탕면');
    console.log(noodleList);
    await _addK_noodle('신라면');
    console.log(noodleList);
};

noodleMaker();

 

 

+

from ChatGPT