Frontend

<Frontend> JavaScript / 비동기 처리

이게왜 2024. 4. 3. 16:07

비동기 처리

2024.04.02 - [IT] - JavaScript / 동기/비동기

 

<Frontend> JavaScript / 동기/비동기

Block / NonBlock , Sync / Async 이번에는 JS에서 가장 중요하면서 어렵고 헷갈리는 개념 중 하나인 "Block / Non - Block", "Sync / Async"를 알아보겠습니다! 머리로는 이해해도 설명해보라고 하면 뭐라 설명할

rezerocodinglife.tistory.com

이전 글에서 다룬 비동기를 처리하는 방법들을 알아보겠습니다.

이번에 다룰 비동기적 코드 결과 처리 방법은 총 3가지입니다.

  • Call Back
  • Promise
  • Async / Await (syntactic sugar)

파이팅!


1. 콜백 (Call Back)

call back은 비동기 작업의 완료 시 호출되는 함수입니다.

주로 call back 함수를 인자로 받아 비동기 작업을 수행하고, 작업이 완료되면 콜백 함수를 호출하여 결과를 처리합니다.

  • 전통적인 비동기 결과 처리 방법
  • 함수를 파라미터로 넘겨 파라미터를 받은 함수에게 실행권을 넘기는 것.
  • call back ≠ 비동기 함수
    • 특정 시점에 실행되는 함수
    • 메서드를 콜백 함수로 사용하면 함수 취급.
// 동기적 콜백
const fruits = ['apple', 'banana', 'cherry']

function syncCallback(item) { 
	console.log(item); 
}

fruits.map(syncCallback);
// 비동기적 콜백
function asyncCallback(){
	console.log('Callback')
}

setTimeout(asyncCallback, 1000);

콜백 지옥(Callback Hell) / 운명의 피라미드 (Pyramid of Doom)

콜백의 단점으로는 콜백 지옥(callback hell)이 발생할 수 있다는 점입니다.

비동기 작업이 연속적으로 중첩되어 복잡한 코드가 생성되는 현상을 말합니다.

fetchData((data) => {
    parseData(data, (parsed) => {
        filterData(parsed, (filtered) => {
            sortData(filtered, (sorted) => {
                console.log(sorted); // 최종 결과 처리
            });
        });
    });
});
  • call back의 결괏값이 그다음 call back 실행에 필요한 경우
  • 코드의 가독성 저하
  • 유지보수성 저하


 

2. 프로미스 (Promise)

promise는 ES6에서 도입된 객체입니다.

비동기 작업의 성공 또는 실패와 같은 결과를 나타내는 데 사용됩니다.

promise는 비동기 작업을 수행하고, 작업이 완료되면 성공 또는 실패에 따라 처리할 수 있도록 해줍니다.

  • call back + Async
  • 비동기 연산을 도와주는 객체
  • 생산자 - 소비자 패턴 (Producer - Customer Pattern)
const promise =  new Promise((resolve, reject) => {
		const isSuccess = true;
		
		if(isSuccess){
				resolve('작업 성공');
		} else {
				reject('작업 실패')
		}
})

Promise 상태

const getUserNameById = (id) => {
  if(id == 1){
    return { success: true, user : {name : 'Kim'} }
  }else {
    return { failed: true, user : {} };
  }
}

const promise = new Promise((resolve, reject) => {
  const id = 1;

  const result = getUserNameById(id); // pending

  if(result.success) { // fulfilled
	  resolve(result.user); 
  }

  if(result.failed) { // reject
	  reject({type : 'Not Found', message : 'id 1 is Not Found'});
  }
})

promise
	.then((user) => console.log(user))
	.catch((error) => console.log(error))
  • 대기 (pending)
    • 비동기 처리 로직이 처리가 되지 않은 상황
  • 성공 (fulfilled)
    • 성공적으로 처리가 완료되어, Promiseresolve를 통해 결과 값을 반환
  • 실패 (rejected)
    • 실패하거나 오류가 발생해 그 원인을 rejected를 통해 반환

Promise 생성

const promise = new Promise((resolve, reject) => {
	// 비 동기 작업 작성
})
  • new 생성자를 통해 생성
  • 생성자는 실행 함수 resolve, reject를 파라미터로 받음
  • resolve
    • 비동기 작업이 성공적을 완료되면 호출
    • 상태 값을 fulfilled 상태로 변경
  • reject
    • 비동기 작업이 실패했을 때 호출
    • 상태 값을 rejected 상태로 변경

Promise 사용

promise
	.then((result) => {
		// 성공시 동작할 코드
	})
	.catch((error) => {
		// 실패시 동작할 코드
	})
	.finally(() => {
		// 성공 유무에 상관 없이 마지막에 실행할 코드
	})
  • then
    • promise가 성공적으로 이행되었을 때 실행될 콜백 함수 등록
  • catch
    • promise가 거부되었을 때 실행 될 콜백 함수 등록
  • finally
    • 성공 / 실패 여부와 관계없이 마지막에 항상 실행되는 콜백

 


Promise Hell / Nested Promise

  • call back hell에서 유래
  • 여러 겹의 중첩된 Promise로 인해 가독성과 유지 보수성이 저하되는 상황
fetchData()
    .then(data => {
        parseData(data)
            .then(parsed => {
                filterData(parsed)
                    .then(filtered => {
                        sortData(filtered)
                            .then(sorted => {
                                console.log(sorted); // 최종 결과 처리
                            })
                            .catch(error => {
                                console.error(error); // sortData 에러 처리
                            });
                    })
                    .catch(error => {
                        console.error(error); // filterData 에러 처리
                    });
            })
            .catch(error => {
                console.error(error); // parseData 에러 처리
            });
    })
    .catch(error => {
        console.error(error); // fetchData 에러 처리
    });

 


Promise Chain

fetchData()
    .then(data => parseData(data))
    .then(parsed => filterData(parsed))
    .then(filtered => sortData(filtered))
    .then(sorted => {
        console.log(sorted); // 최종 결과 처리
    })
    .catch(error => {
        console.error(error); // 에러 처리
    });
  • promise를 사용하는 패턴 중 하나
  • 여러 비동기 promise 작업을 순차적으로 실행하기 위해 사용

3. Async / Await

async / await은 JS에서 비동기 처리를 보다 간편하게 다룰 수 있는 "syntactic sugar"입니다.

async / await은 Promise를 기반으로 하며, 내부적으로는 Promise를 사용하여 비동기 작업을 처리합니다.

 

const asyncFuncfion = async() => {...}

const value1 = await asyncFunction();
const value2 = await asyncFunction();
const value3 = await asyncFunction();
  • async 함수
    • 함수 앞에 async 키워드를 사용하여 정의
    • 함수는 항상 Promise를 반환
    • 함수 내부에서 await 키워드 사용가능
  • await 표현식
    • Promise 앞에서 사용
    • 해당 Promise가 처리될 때까지 async 함수의 실행을 일시 중지시킴
// Promise 
const greetPromise = () => {
  return new Promise((resolve, reject) => {
    resolve('hello')
  })
}

// Async 형태. 기본적으로 Promise를 리턴.
const greetAsync = async() => {
  return 'hello'
}

Async / Await 예외 처리 방식

  • try - catch 문법 사용
const greet = async () => {
	const isSuccess = true;
	
	if(isSuccess){
		return 'Hello'
	}else {
		throw 'Bye'
	}
}

try{
	const greetText = await greet();
	console.log(greetText); // hello
}catch(e){
	console.log(e); // Bye
}

비동기 처리 Callback, Promise 기반

 

Callback 기반 비동기 처리 방식

AJAX (Asynchronuous JavaScript And XML)

  • 자체 빌트인 함수인 XMLHttpRequest를 사용
  • 사용하기에는 너무 복잡한 원시적인 코드
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
 if (xhr.readyState === xhr.DONE) {
  if (xhr.status === 200 || xhr.status === 201) {
   console.log(xhr.responseText);
  } else {
   console.error(xhr.responseText);
  }
 }
};
xhr.open('GET', 'http://localhost');
xhr.send();

 

jQuery AJAX

  • 크로스 브라우저 환경에서 일관된 자바스크립트 문법 제공을 위한 라이브러리
  • DOM, CSS 조작이 매우 간편해지고 기능이 매우 다양
  • AJAX 만을 사용하기 위해서 쓰기에는 너무 거대.
    • 사용 시에는 분명한 목적이 있어야 함.
$.ajax({
   type: 'POST',
   url: url,
   data: data,
   dataType: dataType,
   success: function (res) { console.log(res); },
   error: function (res) {console.log(res); }
});

Promise 기반

Fetch API

  • ES6 지원 비동기 통신을 위한 JS 내장 API
  • 라이브러리가 아니기 때문에 매우 가볍게 동작 가능
try {
  let response = await fetch(url);
  let data = await response.json();
  console.log(data);
} catch(e) {
  console.log("Oops, error", e);
}

 

Axios

  • 서드파티 라이브러리
  • 자동으로 JSON 변환 과정이 포함되어 있어 편리
axios({
    method: 'post',
    url: '/user/12345',
    data: {
        firstName: 'Fred',
        lastName: 'Flintstone'
    }
})
.then(function (response) {
    console.log(response);
})
.catch(function (error) {
    console.log(error);
});