반응형

비동기처리란?

작업의 완료 유무와 상관 없이 동시에 다음 작업을 수행하는 처리 방식을 말한다.

(동기처리는 작업이 반드시 순서대로만 수행되는 처리 방식을 의미한다.)

 

비동기처리를 수행하기 위해 TS(JS)에서는 Callback Function, Promise, Async-Await 등을 이용한다.

각각이 비동기처리를 어떻게 수행하는지 순서대로 알아보자.

 

 

 

 

Callback Function

Callback Function은 특정 조건에서 실행되는 함수로, 특정 시간에 도달하거나 특정 이벤트가 실행됐을 때 시스템에서 호출하는 함수이다.

간략하게만 알아보자.

console.log('Hello Server');

setTimeout((): void => {
    console.log('Hello Server 2');
}, 3000); // 3초

console.log('Hello Server 3');

setTimeout()은 특정 시간 이후에 콜백 함수를 실행시키는 내장함수이다.

만약 위 코드가 동기처리된다면 Hello Server, 2, 3 순서대로 출력되겠지만,

위와 같이 비동기처리된다.

 

위와 같은 Callback Function을 활용한 비동기처리의 문제점은 코드가 복잡해진다는 점이다.

let serverList: string[] = [];

setTimeout((name): void => {
    serverList = [...serverList, name];
    console.log(serverList);
    
    setTimeout((name): void => {
        serverList = [...serverList, name];
        console.log(serverList);
        
        setTimeout((name): void => {
            serverList = [...serverList, name];
            console.log(serverList);
        }, 500, 'name3');
    }, 500, 'name2');   
}, 500, 'name1');

위와 같은 코드는 안으로 심하게 파여 흔히 '콜백 지옥(Callback hell)'이라고 표현한다.

 

 

 

 

Promise

Callback hell을 극복하기 위해 만들어진 것으로,

Pending(대기), Fulfilled(이행), Rejected(실패)의 3가지 상태가 있다.

const condition: boolean = true;

const promise = new Promise((resolve, reject) => {
    if (condition) {
        setTimeout(() => {
            resolve('Success');    
        }, 1000);
        // resolve('Success');
    } else {
        reject(new Error('Error! Condition is false'))
    }
});

promise
    .then((resolveData): void => console.log(resolveData))
    .catch(err => console.log(err));

console.log('test')

위 코드를 통해 Promise의 사용 방법을 알 수 있다.

우선 new Promise를 통해 promise에 Promise 객체를 할당한다.

condition이 true이므로 setTimeout이 실행되는데,

promise.then에서 then은 resolve가 실행된 이후 호출된다.

 

즉 setTimeout()의 조건에 따라 1초가 지나기 전이 Pending(대기)상태이고, 이때제일 아래에 있는 console.log('test')가 실행된다.

이후 1초가 지나 resolve가 실행되면 'Success' 값이 then으로 전달되고 그것을 resolveData라는 매개변수로 받는다.

이 상태가 Fulfilled(이행) 상태이다.

 

만약 condition이 false였다면 reject가 실행되었을 것이고, catch가 호출된다. 이 상태를 Rejected(실패) 라고 한다.

 

조금 복잡한 형태의 코드를 통해

Promise Chaining에 대해 알아보자. (Promise를 연쇄적으로 호출하는 것을 의미한다.)

 

const restaurant = (callback: () => void, time: number) => {
    setTimeout(callback, time);
};

const order = (): Promise<string> => {		// order 객체가 string을 반환하는 Promise 객체라는 뜻이다.
    return new Promise((resolve, reject) => {	// Promise 객체를 반환한다
        restaurant(() => {						// Promise 내부 함수
            console.log('레스토랑 진행 상황 - 음식 주문');
            resolve('음식 주문 시작');			
        }, 1000);
    });
}

const cook = (progress: string): Promise<string> => {	// cook은 string인 progress라는 매개변수를 받아, string을 반환하는 Promise 객체라는 뜻.
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 조리');
        restaurant(() => {
            resolve(`${progress} -> 음식 조리 중`);	// 전달받은 progress에 텍스트를 붙여 resolve로 반환한다.
        }, 2000);
    });
}

const serving = (progress: string): Promise<string>  => {
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 서빙');
        restaurant(()=> {
            resolve(`${progress} -> 음식 서빙 중`);
        }, 3000);
    });
}

const eat = (progress: string): Promise<string> => {
    return new Promise((resolve, reject) => {
        console.log('레스토랑 진행 상황 - 음식 먹기');
        restaurant(() => {
            resolve(`${progress} -> 음식 먹는 중`);
        }, 4000);
    });
}

order()	// order() 실행
    .then(progress => cook(progress))	// order 내부에서 resolve가 실행되어 반환되는 값이 progress로 들어간다. progress => cook(progress)는 함수를 then의 매개변수로 넣은 것.
    .then(progress => serving(progress))
    .then(progress => eat(progress))
    .then(progress => console.log(progress));

progress가 연쇄적으로 전달된다.

 

 

 

 

Async - Await

ES2017부터 제공되는 혁신적인 기능! 이라고 한다.

코드를 짤 때 동기식 코드처럼 사고하고 코딩할 수 있게 되었다.

 

우선 함수 선언부터 알아보자.

// 함수 표현식
const asyncFunction1 = async () => {

}

// // 함수 선언식
async function asyncFunction2() {

}

위와 같이 선언한다.

 

let asyncFunc1 = (msg: string): Promise<string> => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`asyncFunc1 - ${msg}`);
        }, 1000);
    });
};

let asyncFunc2 = (msg: string): Promise<string> => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(`asyncFunc2 - ${msg}`);
        }, 1500);
    });
};

const asyncMain = async (): Promise<void> => {
    let result = await asyncFunc1('hi');
    console.log(result);
    result = await asyncFunc2('hello');
    console.log(result);
};

const promiseMain = (): void => {
    asyncFunc1('hi').then((result: string) => {
        console.log(result);
        return asyncFunc2('hello');
    }).then((result: string) => {
        console.log(result);
    });
};

asyncMain();

promiseMain();

await은 Promise가 실행될 때까지 기다리겠다는 뜻이다.

익숙한 동기 코드의 실행 방식처럼, 실행 여부를 then, catch로 분기하지 않고 코딩할 수 있다.

 

 

 

 

 

 

반응형

+ Recent posts